OPENXR Scol plugin
sOpenXR.cpp
1/*
2-----------------------------------------------------------------------------
3This source file is part of OpenSpace3D
4For the latest info, see http://www.openspace3d.com
5
6Copyright (c) 2022 I-maginer
7
8This program is free software; you can redistribute it and/or modify it under
9the terms of the GNU Lesser General Public License as published by the Free Software
10Foundation; either version 2 of the License, or (at your option) any later
11version.
12
13This program is distributed in the hope that it will be useful, but WITHOUT
14ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16
17You should have received a copy of the GNU Lesser General Public License along with
18this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19Place - Suite 330, Boston, MA 02111-1307, USA, or go to
20http://www.gnu.org/copyleft/lesser.txt
21
22-----------------------------------------------------------------------------
23*/
24
25#include "sOpenXR.h"
26#include <set>
27#include <iostream>
28
29#if !defined(XR_USE_PLATFORM_WIN32)
30# define strcpy_s(dest, source) strncpy((dest), (source), sizeof(dest))
31#endif
32
33//Common functions
34bool xr_result(XrInstance instance, XrResult result, const char* format, ...)
35{
36 if (XR_SUCCEEDED(result))
37 return true;
38
39 char resultString[XR_MAX_RESULT_STRING_SIZE];
40
41 if (instance != XR_NULL_HANDLE)
42 {
43 xrResultToString(instance, result, resultString);
44
45 size_t len1 = strlen(format);
46 size_t len2 = strlen(resultString) + 1;
47 char *formatRes = new char[len1 + len2 + 15]; // + "[OPENXR]: []\n"
48 sprintf(formatRes, "[OPENXR]: %s [%s]\n", format, resultString);
49
50 va_list args;
51 va_start(args, format);
52 MMechostr(MSKRUNTIME, formatRes, args);
53 va_end(args);
54
55 delete[] formatRes;
56 }
57 else
58 {
59 size_t len1 = strlen(format);
60 char *formatRes = new char[len1 + 12]; // + "[OPENXR]: \n"
61 sprintf(formatRes, "[OPENXR]: %s\n", format);
62 MMechostr(MSKRUNTIME, formatRes);
63 delete[] formatRes;
64 }
65 return false;
66}
67
68void get_instance_properties(XrInstance instance)
69{
70 XrResult result;
71 XrInstanceProperties instance_props = { XR_TYPE_INSTANCE_PROPERTIES, XR_NULL_HANDLE };
72 std::stringstream buf;
73
74 result = xrGetInstanceProperties(instance, &instance_props);
75 if (!xr_result(XR_NULL_HANDLE, result, "Failed to get instance info"))
76 return;
77
78 buf << "[OPENXR]: Runtime Name: " << instance_props.runtimeName << "\n"
79 << "Runtime Version: " << XR_VERSION_MAJOR(instance_props.runtimeVersion) << "." << XR_VERSION_MINOR(instance_props.runtimeVersion) << "." << XR_VERSION_PATCH(instance_props.runtimeVersion) << std::endl;
80 MMechostr(MSKRUNTIME, buf.str().c_str());
81}
82
83void print_system_properties(XrSystemProperties* system_properties, bool hand_tracking_ext)
84{
85 std::stringstream buf;
86 buf << "[OPENXR]: System properties for system " << system_properties->systemId << " \""
87 << system_properties->systemName << "\", vendor ID " << system_properties->vendorId << "\n"
88 << "\tMax layers : " << system_properties->graphicsProperties.maxLayerCount << "\n"
89 << "\tMax swapchain height: " << system_properties->graphicsProperties.maxSwapchainImageHeight << "\n"
90 << "\tMax swapchain width : " << system_properties->graphicsProperties.maxSwapchainImageWidth << "\n"
91 << "\tOrientation Tracking: " << system_properties->trackingProperties.orientationTracking << "\n"
92 << "\tPosition Tracking : " << system_properties->trackingProperties.positionTracking;
93
94 if (hand_tracking_ext)
95 {
96 //XrSystemHandTrackingPropertiesEXT* ht = (XrSystemHandTrackingPropertiesEXT*)system_properties->next;
97 buf << "\tHand Tracking : true"; // << ht->supportsHandTracking;
98 }
99 buf << std::endl;
100 MMechostr(MSKRUNTIME, buf.str().c_str());
101}
102
103// Initialize singleton
104sOpenXr* sOpenXr::_singleton = nullptr;
105
106sOpenXr::sOpenXr(RenderSystem rsys, float scaleRatio)
107{
108 mRendererType = rsys;
109 mAppName = "Scol OpenXR plugin";
110 mInstance = XR_NULL_HANDLE;
111 mSession = XR_NULL_HANDLE;
112 mConnected = false;
113 mPaused = false;
114 mFrameIndex = 1;
115 mLastIPD = 0.064f;
116 mPredictedDisplayTime = 0;
117 mScaleRatio = scaleRatio;
118 mSessionRunning = false;
119 mRequestRestart = false;
120 mPoseValid = false;
121 mViewType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
122 mPassthroughEnable = false;
123 mSkipNextFrame = false;
124 mSystemId = XR_NULL_SYSTEM_ID;
125 mSession = XR_NULL_HANDLE;
126 mInstance = XR_NULL_HANDLE;
127 mLastControllerType = ControllerType::CLASSIC_CONTROLLER;
128 mExtensionsEnable = ExtensionType::EXTENSION_NONE;
129
130 for (unsigned int i = 0; i < MAX_VR_CONTROLLERS; i++)
131 mControllerType[i] = ControllerType::CLASSIC_CONTROLLER;
132
133 for (unsigned int i = 0; i < 2; i++)
134 mFov[i] = 0;
135
136 mBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE;
137 mHandTracking.gestures = false;
138 mHandTracking.supported = false;
139 mHandTracking.system_supported = false;
140
141 mPassthroughFB.supported = false;
142 mPassthroughFB.available = false;
143 mPassthroughFB.passthroughLayer = XR_NULL_HANDLE;
144 mPassthroughFB.pfnXrCreatePassthroughFBX = XR_NULL_HANDLE;
145 mPassthroughFB.pfnXrDestroyPassthroughFBX = XR_NULL_HANDLE;
146 mPassthroughFB.pfnXrPassthroughPauseFBX = XR_NULL_HANDLE;
147 mPassthroughFB.pfnXrPassthroughStartFBX = XR_NULL_HANDLE;
148 mPassthroughFB.pfnXrCreatePassthroughLayerFBX = XR_NULL_HANDLE;
149 mPassthroughFB.pfnXrDestroyPassthroughLayerFBX = XR_NULL_HANDLE;
150 mPassthroughFB.pfnXrPassthroughLayerPauseFBX = XR_NULL_HANDLE;
151 mPassthroughFB.pfnXrPassthroughLayerResumeFBX = XR_NULL_HANDLE;
152 mPassthroughFB.passthroughFeature = XR_NULL_HANDLE;
153
154 mPassthroughHTC.supported = false;
155 mPassthroughHTC.passthroughFeature = XR_NULL_HANDLE;
156 mPassthroughHTC.pfnXrCreatePassthroughHTC = XR_NULL_HANDLE;
157 mPassthroughHTC.pfnXrDestroyPassthroughHTC = XR_NULL_HANDLE;
158
159 //initialize platform
160 std::shared_ptr<PlatformData> data = std::make_shared<PlatformData>();
161
162#if defined(XR_USE_PLATFORM_WIN32)
163 mPlatform = CreatePlatform(data);
164#elif defined(XR_USE_PLATFORM_ANDROID)
165 struct android_app* androidApp = (struct android_app*)SCgetExtra("this_inst");
166
167 JNIEnv* Env;
168 androidApp->activity->vm->AttachCurrentThread(&Env, nullptr);
169
170 data->applicationVM = androidApp->activity->vm;
171 data->applicationActivity = androidApp->activity->clazz;
172
173 mPlatform = CreatePlatform(data);
174
175 // Initialize the loader for this platform
176 PFN_xrInitializeLoaderKHR initializeLoader = XR_NULL_HANDLE;
177 if (XR_SUCCEEDED(xrGetInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)(&initializeLoader))))
178 {
179 XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid;
180 memset(&loaderInitInfoAndroid, 0, sizeof(loaderInitInfoAndroid));
181 loaderInitInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
182 loaderInitInfoAndroid.next = XR_NULL_HANDLE;
183 loaderInitInfoAndroid.applicationVM = androidApp->activity->vm;
184 loaderInitInfoAndroid.applicationContext = androidApp->activity->clazz;
185 initializeLoader((const XrLoaderInitInfoBaseHeaderKHR*)&loaderInitInfoAndroid);
186 }
187#endif
188
189 //initialize renderer
190 mRenderer = CreateRenderer(mRendererType);
191}
192
194{
195 Disconnect();
196
197#if defined(XR_USE_PLATFORM_ANDROID)
198 struct android_app* androidApp = (struct android_app*)SCgetExtra("this_inst");
199 androidApp->activity->vm->DetachCurrentThread();
200#endif
201}
202
203void sOpenXr::SetRenderer(RenderSystem renderer)
204{
205 if (mRendererType != renderer)
206 {
207 mRendererType = renderer;
208 if (mConnected)
209 {
210 Disconnect();
211 mRenderer = CreateRenderer(renderer);
212 Connect();
213 }
214 else
215 {
216 mRenderer = CreateRenderer(renderer);
217 }
218 }
219}
220
221void sOpenXr::SetExtensions(int extensions)
222{
223 if (extensions != mExtensionsEnable)
224 {
225 mExtensionsEnable = extensions;
226
227 if (mConnected)
228 {
229 Disconnect();
230 Connect();
231 }
232 }
233}
234
235
236void sOpenXr::SetAppName(std::string name)
237{
238 mAppName = name;
239 if (mConnected)
240 {
241 Disconnect();
242 Connect();
243 }
244}
245
246bool sOpenXr::SetSceneBlendMode(XrEnvironmentBlendMode mode)
247{
248 uint32_t blendCount;
249 xrEnumerateEnvironmentBlendModes(mInstance, mSystemId, mViewType, 0, &blendCount, nullptr);
250
251 if (blendCount > 0)
252 {
253 std::vector<XrEnvironmentBlendMode> blendModes(blendCount);
254 xrEnumerateEnvironmentBlendModes(mInstance, mSystemId, mViewType, blendCount, &blendCount, blendModes.data());
255
256 for (const auto& blendMode : blendModes)
257 {
258 if (blendMode == mode)
259 {
260 mBlendMode = blendMode;
261 return true;
262 }
263 }
264 }
265 return false;
266}
267
268int sOpenXr::GetExtensions()
269{
270 return mExtensionsEnable;
271}
272
273bool sOpenXr::initialize()
274{
275 XrResult result;
276 mRequestRestart = false;
277
278 if (!mRenderer->Setup())
279 return false;
280
281 // xrEnumerate*() functions are usually called once with CapacityInput = 0.
282 // The function will write the required amount into CountOutput. We then have
283 // to allocate an array to hold CountOutput elements and call the function
284 // with CountOutput as CapacityInput.
285 uint32_t ext_count = 0;
286 result = xrEnumerateInstanceExtensionProperties(XR_NULL_HANDLE, 0, &ext_count, XR_NULL_HANDLE);
287
288 if (!xr_result(XR_NULL_HANDLE, result, "Failed to enumerate number of extension properties"))
289 return false;
290
291 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime supports %d extensions\n", ext_count);
292
293 std::vector<XrExtensionProperties> extensionProperties(ext_count, { XR_TYPE_EXTENSION_PROPERTIES, XR_NULL_HANDLE });
294 result = xrEnumerateInstanceExtensionProperties(XR_NULL_HANDLE, ext_count, &ext_count, extensionProperties.data());
295 if (!xr_result(XR_NULL_HANDLE, result, "Failed to enumerate extension properties"))
296 return false;
297
298 //Hack Pico to disable not totally functional hand tracking
299 bool rendererSupport = false;
300 mHandTracking.supported = false;
301 mHandTracking.gestures = false;
302 mPassthroughFB.supported = false;
303 mPassthroughHTC.supported = false;
304 mPicoController.supported = false;
305
306 for (uint32_t i = 0; i < ext_count; i++)
307 {
308 MMechostr(MSKRUNTIME, "\t%s v%d\n", extensionProperties[i].extensionName, extensionProperties[i].extensionVersion);
309
310 if (strcmp(mRenderer->GetExtension(), extensionProperties[i].extensionName) == 0)
311 rendererSupport = true;
312
313 if ((strcmp(XR_EXT_HAND_TRACKING_EXTENSION_NAME, extensionProperties[i].extensionName) == 0) && (mExtensionsEnable & EXTENSION_HAND_TRACKING))
314 {
315 mHandTracking.supported = true;
316 mHandTracking.trackers[VrController::Left] = 0;
317 mHandTracking.trackers[VrController::Right] = 0;
318 }
319
320 if ((strcmp(XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME, extensionProperties[i].extensionName) == 0) && (mExtensionsEnable & EXTENSION_HAND_TRACKING))
321 mHandTracking.gestures = true;
322
323 if ((strcmp(XR_FB_PASSTHROUGH_EXTENSION_NAME, extensionProperties[i].extensionName) == 0) && (mExtensionsEnable & EXTENSION_PASSTHROUGH))
324 mPassthroughFB.supported = true;
325
326 if ((strcmp(XR_HTC_PASSTHROUGH_EXTENSION_NAME, extensionProperties[i].extensionName) == 0) && (mExtensionsEnable & EXTENSION_PASSTHROUGH))
327 mPassthroughHTC.supported = true;
328
329 if (strcmp(XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME, extensionProperties[i].extensionName) == 0)
330 mPicoController.supported = true;
331 }
332
333 // A graphics extension like OpenGL is required to draw anything in VR
334 if (!rendererSupport)
335 {
336 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime does not support Renderer API extension!\n");
337 return false;
338 }
339
340 // --- Create XrInstance
341 std::vector<const char*> extensions;
342
343 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime supports extensions:\n");
344 MMechostr(MSKRUNTIME, "\t%s\n", mRenderer->GetExtension());
345 extensions.push_back(mRenderer->GetExtension());
346
347 MMechostr(MSKRUNTIME, "\t%s: %d\n", XR_EXT_HAND_TRACKING_EXTENSION_NAME, mHandTracking.supported);
348 MMechostr(MSKRUNTIME, "\t%s: %d\n", XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME, mHandTracking.gestures);
349 MMechostr(MSKRUNTIME, "\t%s: %d\n", XR_FB_PASSTHROUGH_EXTENSION_NAME, mPassthroughFB.supported);
350 MMechostr(MSKRUNTIME, "\t%s: %d\n", XR_HTC_PASSTHROUGH_EXTENSION_NAME, mPassthroughHTC.supported);
351 MMechostr(MSKRUNTIME, "\t%s: %d\n", XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME, mPicoController.supported);
352
353 if (mHandTracking.supported)
354 extensions.push_back(XR_EXT_HAND_TRACKING_EXTENSION_NAME);
355
356 if (mHandTracking.gestures)
357 extensions.push_back(XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME);
358
359 if (mPassthroughFB.supported)
360 extensions.push_back(XR_FB_PASSTHROUGH_EXTENSION_NAME);
361
362 if (mPassthroughHTC.supported)
363 extensions.push_back(XR_HTC_PASSTHROUGH_EXTENSION_NAME);
364
365 if (mPicoController.supported)
366 extensions.push_back(XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME);
367
368 // Transform platform and graphics extension std::strings to C strings.
369 const std::vector<std::string> platformExtensions = mPlatform->GetInstanceExtensions();
370 std::transform(platformExtensions.begin(), platformExtensions.end(), std::back_inserter(extensions),
371 [](const std::string& ext) { return ext.c_str(); });
372
373 XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
374 createInfo.next = mPlatform->GetInstanceCreateExtension();
375 createInfo.enabledExtensionCount = (uint32_t)extensions.size();
376 createInfo.enabledExtensionNames = extensions.data();
377
378 strcpy_s(createInfo.applicationInfo.applicationName, mAppName.c_str());
379 createInfo.applicationInfo.apiVersion = XR_CURRENT_API_VERSION;
380 strcpy_s(createInfo.applicationInfo.engineName, "OpenSpace3D - Scol Engine");
381 createInfo.applicationInfo.applicationVersion = 1;
382
383 result = xrCreateInstance(&createInfo, &mInstance);
384 if (!xr_result(XR_NULL_HANDLE, result, "Failed to create XR instance."))
385 return false;
386
387 // Optionally get runtime name and version
388 get_instance_properties(mInstance);
389
390 // --- Create XrSystem
391 XrSystemGetInfo system_get_info = { XR_TYPE_SYSTEM_GET_INFO, XR_NULL_HANDLE, XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY };
392
393 result = xrGetSystem(mInstance, &system_get_info, &mSystemId);
394 if (!xr_result(mInstance, result, "Failed to get system for HMD form factor.") || (mSystemId == XR_NULL_SYSTEM_ID))
395 {
396 xrDestroyInstance(mInstance);
397 return false;
398 }
399
400 MMechostr(MSKRUNTIME, "[OPENXR]: Successfully got XrSystem with id %lu for HMD form factor\n", mSystemId);
401
402 // checking system properties is generally optional, but we are interested in hand tracking
403 // support
404 XrSystemHandTrackingPropertiesEXT hand_tracking_props = { XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT };
405 XrSystemPassthroughProperties2FB passthrough_props{XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES_FB};
406 XrSystemProperties system_props = { XR_TYPE_SYSTEM_PROPERTIES };
407
408 if (mHandTracking.supported)
409 {
410 if (mPassthroughFB.supported)
411 hand_tracking_props.next = &passthrough_props;
412
413 system_props.next = &hand_tracking_props;
414 }
415 else if (mPassthroughFB.supported)
416 {
417 system_props.next = &passthrough_props;
418 }
419
420 result = xrGetSystemProperties(mInstance, mSystemId, &system_props);
421 if (!xr_result(mInstance, result, "Failed to get System properties"))
422 {
423 xrDestroyInstance(mInstance);
424 return false;
425 }
426
427 mHandTracking.system_supported = mHandTracking.supported && hand_tracking_props.supportsHandTracking;
428 mHandTracking.gestures = mHandTracking.gestures && mHandTracking.system_supported;
429 //mPassthroughFB.supported = mPassthroughFB.supported && (passthrough_props.capabilities & XR_PASSTHROUGH_CAPABILITY_BIT_FB);
430
431 mDeviceName = system_props.systemName;
432 print_system_properties(&system_props, mHandTracking.supported);
433 mIsPico = (mDeviceName == "SteamVR/OpenXR : pico") || (mDeviceName == "PICO 4") || (mDeviceName == "PICO 4 Pro") || (mDeviceName == "Pico Neo 3 Pro Eye");
434
435 printSupportedViewConfigs();
436
437 uint32_t view_count = 0;
438 result = xrEnumerateViewConfigurationViews(mInstance, mSystemId, mViewType, 0, &view_count, XR_NULL_HANDLE);
439 if (!xr_result(mInstance, result, "Failed to get view configuration view count!"))
440 {
441 xrDestroyInstance(mInstance);
442 return false;
443 }
444
445 mViewconfigViews.resize(view_count, { XR_TYPE_VIEW_CONFIGURATION_VIEW, XR_NULL_HANDLE });
446 mViews.resize(view_count, { XR_TYPE_VIEW });
447
448 result = xrEnumerateViewConfigurationViews(mInstance, mSystemId, mViewType, view_count, &view_count, mViewconfigViews.data());
449 if (!xr_result(mInstance, result, "Failed to enumerate view configuration views!"))
450 {
451 xrDestroyInstance(mInstance);
452 return false;
453 }
454
455 printViewconfigViewInfo();
456
457 if (!mRenderer->Check(mSystemId, mInstance))
458 {
459 xrDestroyInstance(mInstance);
460 return false;
461 }
462
463 // --- Create session
464 mState = XR_SESSION_STATE_UNKNOWN;
465 XrSessionCreateInfo session_create_info = { XR_TYPE_SESSION_CREATE_INFO, mRenderer->GetBindings(), 0, mSystemId };
466
467 result = xrCreateSession(mInstance, &session_create_info, &mSession);
468 if (!xr_result(mInstance, result, "Failed to create session"))
469 {
470 xrDestroyInstance(mInstance);
471 return false;
472 }
473
474 MMechostr(MSKRUNTIME, "[OPENXR]: Successfully created a session!\n");
475
476 if (mHandTracking.system_supported)
477 {
478 result = xrGetInstanceProcAddr(mInstance, "xrLocateHandJointsEXT", reinterpret_cast<PFN_xrVoidFunction *>(&mHandTracking.pfnLocateHandJointsEXT));
479 xr_result(mInstance, result, "Failed to get xrLocateHandJointsEXT function!");
480
481 PFN_xrCreateHandTrackerEXT pfnCreateHandTrackerEXT = XR_NULL_HANDLE;
482 result = xrGetInstanceProcAddr(mInstance, "xrCreateHandTrackerEXT", reinterpret_cast<PFN_xrVoidFunction *>(&pfnCreateHandTrackerEXT));
483
484 if (!xr_result(mInstance, result, "Failed to get xrCreateHandTrackerEXT function!"))
485 mHandTracking.system_supported = false;
486
487 XrHandTrackerCreateInfoEXT hand_tracker_create_info_l = { XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, XR_NULL_HANDLE, XR_HAND_LEFT_EXT, XR_HAND_JOINT_SET_DEFAULT_EXT };
488 result = pfnCreateHandTrackerEXT(mSession, &hand_tracker_create_info_l, &mHandTracking.trackers[VrController::Left]);
489 if (!xr_result(mInstance, result, "Failed to create left hand tracker"))
490 mHandTracking.system_supported = false;
491
492 MMechostr(MSKRUNTIME, "[OPENXR]: Created hand tracker for left hand\n");
493
494 XrHandTrackerCreateInfoEXT hand_tracker_create_info_r = { XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, XR_NULL_HANDLE, XR_HAND_RIGHT_EXT, XR_HAND_JOINT_SET_DEFAULT_EXT };
495 result = pfnCreateHandTrackerEXT(mSession, &hand_tracker_create_info_r, &mHandTracking.trackers[VrController::Right]);
496 if (!xr_result(mInstance, result, "Failed to create right hand tracker"))
497 mHandTracking.system_supported = false;
498
499 MMechostr(MSKRUNTIME, "[OPENXR]: Created hand tracker for right hand\n");
500 }
501
502 if (mPassthroughFB.supported)
503 {
504 if (!xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrCreatePassthroughFB",
505 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrCreatePassthroughFBX)),
506 "Failed to obtain the function pointer for xrCreatePassthroughFB") ||
507
508 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrDestroyPassthroughFB",
509 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrDestroyPassthroughFBX)),
510 "Failed to obtain the function pointer for xrDestroyPassthroughFB") ||
511
512 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrCreatePassthroughLayerFB",
513 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrCreatePassthroughLayerFBX)),
514 "Failed to obtain the function pointer for xrCreatePassthroughLayerFBX") ||
515
516 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrPassthroughStartFB",
517 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrPassthroughStartFBX)),
518 "Failed to obtain the function pointer for xrPassthroughStartFB") ||
519
520 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrPassthroughPauseFB",
521 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrPassthroughPauseFBX)),
522 "Failed to obtain the function pointer for xrPassthroughPauseFB") ||
523
524 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrPassthroughLayerResumeFB",
525 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrPassthroughLayerResumeFBX)),
526 "Failed to obtain the function pointer for xrPassthroughLayerResumeFB") ||
527
528 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrPassthroughLayerPauseFB",
529 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrPassthroughLayerPauseFBX)),
530 "Failed to obtain the function pointer for xrPassthroughLayerPauseFB") ||
531
532 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrDestroyPassthroughLayerFB",
533 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrDestroyPassthroughLayerFBX)),
534 "Failed to obtain the function pointer for xrDestroyPassthroughLayerFB") ||
535
536 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrPassthroughLayerSetStyleFB",
537 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughFB.pfnXrPassthroughLayerSetStyleFB)),
538 "Failed to obtain the function pointer for xrPassthroughLayerSetStyleFB"))
539 mPassthroughFB.supported = false;
540 }
541
542 if (mPassthroughHTC.supported)
543 {
544 if (!xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrCreatePassthroughHTC",
545 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughHTC.pfnXrCreatePassthroughHTC)),
546 "Failed to obtain the function pointer for xrCreatePassthroughHTC") ||
547
548 !xr_result(mInstance, xrGetInstanceProcAddr(mInstance, "xrDestroyPassthroughHTC",
549 reinterpret_cast<PFN_xrVoidFunction *>(&mPassthroughHTC.pfnXrDestroyPassthroughHTC)),
550 "Failed to obtain the function pointer for xrDestroyPassthroughHTC"))
551 mPassthroughHTC.supported = false;
552 }
553
554 printReferenceSpaces();
555
556 XrReferenceSpaceCreateInfo play_space_create_info = { XR_TYPE_REFERENCE_SPACE_CREATE_INFO, XR_NULL_HANDLE, XR_REFERENCE_SPACE_TYPE_STAGE, identity_pose };
557
558 result = xrCreateReferenceSpace(mSession, &play_space_create_info, &mPlaySpace);
559 if (!xr_result(mInstance, result, "Failed to create play space!"))
560 {
561 xrDestroySession(mSession);
562 xrDestroyInstance(mInstance);
563 return false;
564 }
565
566 XrReferenceSpaceCreateInfo view_space_info = { XR_TYPE_REFERENCE_SPACE_CREATE_INFO, XR_NULL_HANDLE, XR_REFERENCE_SPACE_TYPE_VIEW, identity_pose };
567 result = xrCreateReferenceSpace(mSession, &view_space_info, &mViewSpace);
568 if (!xr_result(mInstance, result, "Failed to create view space!"))
569 {
570 xrDestroySession(mSession);
571 xrDestroyInstance(mInstance);
572 return false;
573 }
574
575 // --- Create Swapchains
576 int64_t swapchainFormat = 0;
577 if (!mRenderer->GetSwapchainFormat(mInstance, mSession, swapchainFormat))
578 {
579 xrDestroySession(mSession);
580 xrDestroyInstance(mInstance);
581 return false;
582 }
583
584 /* All OpenGL textures that will be submitted in xrEndFrame are created by the runtime here.
585 * The runtime will give us a number (not controlled by us) of OpenGL textures per swapchain
586 * and tell us with xrAcquireSwapchainImage, which of those we can render to per frame.
587 * Here we use one swapchain per view (eye), and for example 3 ("triple buffering") images per
588 * swapchain.
589 */
590 mSwapchains.resize(view_count);
591
592 //if (!mFovMutable)
593 // mTextureBuffer.resize(view_count);
594
595 // A stereo view config implies two views, but our code is set up for a dynamic amount of views.
596 // So we need to allocate a bunch of memory for data structures dynamically.
597 mProjectionViews.resize(view_count);
598
599 for (uint32_t i = 0; i < view_count; i++)
600 {
601 XrSwapchainCreateInfo swapchain_create_info;
602 swapchain_create_info.type = XR_TYPE_SWAPCHAIN_CREATE_INFO;
603 swapchain_create_info.usageFlags = XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT;
604 swapchain_create_info.createFlags = 0;
605 swapchain_create_info.format = swapchainFormat;
606 swapchain_create_info.sampleCount = mViewconfigViews[i].recommendedSwapchainSampleCount;
607 swapchain_create_info.width = static_cast<uint32_t>((float)mViewconfigViews[i].recommendedImageRectWidth * mScaleRatio);
608 swapchain_create_info.height = static_cast<uint32_t>((float)mViewconfigViews[i].recommendedImageRectHeight * mScaleRatio);
609 swapchain_create_info.faceCount = 1;
610 swapchain_create_info.arraySize = 1;
611 swapchain_create_info.mipCount = 1;
612 swapchain_create_info.next = XR_NULL_HANDLE;
613
614 result = xrCreateSwapchain(mSession, &swapchain_create_info, &mSwapchains[i]);
615 if (!xr_result(mInstance, result, "Failed to create swapchain %d!", i))
616 {
617 xrDestroySession(mSession);
618 xrDestroyInstance(mInstance);
619 return false;
620 }
621
622 if (!mRenderer->CreateSwapchainImage(mInstance, view_count, mSwapchains[i], i))
623 {
624 xrDestroySession(mSession);
625 xrDestroyInstance(mInstance);
626 return false;
627 }
628
629 mProjectionViews[i].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
630 mProjectionViews[i].next = XR_NULL_HANDLE;
631
632 mProjectionViews[i].subImage.swapchain = mSwapchains[i];
633 mProjectionViews[i].subImage.imageArrayIndex = 0;
634 mProjectionViews[i].subImage.imageRect.offset = { 0, 0 };
635 mProjectionViews[i].subImage.imageRect.extent = { static_cast<int32_t>(swapchain_create_info.width), static_cast<int32_t>(swapchain_create_info.height) };
636
637 // projection_views[i].pose (and fov) have to be filled every frame in frame loop
638 }
639
640 uint32_t blendCount;
641 result = xrEnumerateEnvironmentBlendModes(mInstance, mSystemId, mViewType, 0, &blendCount, nullptr);
642
643 if (blendCount > 0)
644 {
645 std::set<XrEnvironmentBlendMode> lookForBlendModes{ XR_ENVIRONMENT_BLEND_MODE_OPAQUE, XR_ENVIRONMENT_BLEND_MODE_ADDITIVE, XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND };
646 std::vector<XrEnvironmentBlendMode> blendModes(blendCount);
647 result = xrEnumerateEnvironmentBlendModes(mInstance, mSystemId, mViewType, blendCount, &blendCount, blendModes.data());
648
649 for (const auto& blendMode : blendModes)
650 {
651 if (mBlendMode == blendMode)
652 break;
653 else if (lookForBlendModes.count(blendMode))
654 mBlendMode = blendMode;
655 }
656 }
657
658 if (!initControllers())
659 {
660 xrDestroySession(mSession);
661 xrDestroyInstance(mInstance);
662 return false;
663 }
664
665 // Create the Passthrough feature
666 createPassthrough();
667
668 return true;
669}
670
671void sOpenXr::cleanup()
672{
673 XrResult result;
674
675 if (mInstance != XR_NULL_HANDLE)
676 {
677 if (mSession != XR_NULL_HANDLE)
678 xrEndSession(mSession);
679
680 if (mHandTracking.system_supported)
681 {
682 PFN_xrDestroyHandTrackerEXT pfnDestroyHandTrackerEXT = XR_NULL_HANDLE;
683 result = xrGetInstanceProcAddr(mInstance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)&pfnDestroyHandTrackerEXT);
684 if (xr_result(mInstance, result, "Failed to get xrDestroyHandTrackerEXT function!"))
685 {
686 if (mHandTracking.trackers[VrController::Left])
687 {
688 result = pfnDestroyHandTrackerEXT(mHandTracking.trackers[VrController::Left]);
689 if (xr_result(mInstance, result, "Failed to destroy left hand tracker"))
690 MMechostr(MSKRUNTIME, "[OPENXR]: Destroyed hand tracker for left hand\n");
691 }
692
693 if (mHandTracking.trackers[VrController::Right])
694 {
695 result = pfnDestroyHandTrackerEXT(mHandTracking.trackers[VrController::Right]);
696 if (xr_result(mInstance, result, "Failed to destroy left hand tracker"))
697 MMechostr(MSKRUNTIME, "[OPENXR]: Destroyed hand tracker for left hand\n");
698 }
699 }
700 }
701
702 cleanPassthrough();
703
704 if (mInput.actionSet != XR_NULL_HANDLE)
705 {
706 for (auto hand : { VrController::Left, VrController::Right })
707 xrDestroySpace(mInput.handSpace[hand]);
708
709 xrDestroyActionSet(mInput.actionSet);
710 }
711
712 xrDestroySpace(mPlaySpace);
713 xrDestroySpace(mViewSpace);
714
715 if (mSession != XR_NULL_HANDLE)
716 {
717 xrDestroySession(mSession);
718 mSession = XR_NULL_HANDLE;
719 }
720 }
721
722 if (mInstance != XR_NULL_HANDLE)
723 {
724 xrDestroyInstance(mInstance);
725 mInstance = XR_NULL_HANDLE;
726 }
727 mPoseValid = false;
728}
729
730void sOpenXr::printSupportedViewConfigs()
731{
732 XrResult result;
733
734 uint32_t view_config_count;
735 result = xrEnumerateViewConfigurations(mInstance, mSystemId, 0, &view_config_count, XR_NULL_HANDLE);
736 if (!xr_result(mInstance, result, "Failed to get view configuration count"))
737 return;
738
739 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime supports %d view configurations", view_config_count);
740
741 std::vector<XrViewConfigurationType> view_configs(view_config_count);
742 result = xrEnumerateViewConfigurations(mInstance, mSystemId, view_config_count, &view_config_count, view_configs.data());
743 if (!xr_result(mInstance, result, "Failed to enumerate view configurations!"))
744 return;
745
746 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime supports view configurations:\n");
747 for (uint32_t i = 0; i < view_config_count; ++i)
748 {
749 XrViewConfigurationProperties props = { XR_TYPE_VIEW_CONFIGURATION_PROPERTIES, XR_NULL_HANDLE };
750
751 result = xrGetViewConfigurationProperties(mInstance, mSystemId, view_configs[i], &props);
752 if (!xr_result(mInstance, result, "Failed to get view configuration info %d!", i))
753 return;
754
755 //mFovMutable = props.fovMutable == XR_TRUE ? true : false;
756 //std::cout << props.viewConfigurationType << ": FOV mutable: " << props.fovMutable << "\n";
757 }
758}
759
760void sOpenXr::printViewconfigViewInfo()
761{
762 for (uint32_t i = 0; i < mViewconfigViews.size(); i++)
763 {
764 MMechostr(MSKRUNTIME, "[OPENXR]: View Configuration View %d:\n", i);
765 MMechostr(MSKRUNTIME, "\tResolution : Recommended %dx%d, Max: %dx%d\n",
766 mViewconfigViews[0].recommendedImageRectWidth,
767 mViewconfigViews[0].recommendedImageRectHeight,
768 mViewconfigViews[0].maxImageRectWidth,
769 mViewconfigViews[0].maxImageRectHeight);
770 MMechostr(MSKRUNTIME, "\tSwapchain Samples: Recommended: %d, Max: %d)\n",
771 mViewconfigViews[0].recommendedSwapchainSampleCount,
772 mViewconfigViews[0].maxSwapchainSampleCount);
773 }
774}
775
776void sOpenXr::printReferenceSpaces()
777{
778 XrResult result;
779
780 uint32_t ref_space_count;
781 result = xrEnumerateReferenceSpaces(mSession, 0, &ref_space_count, XR_NULL_HANDLE);
782 if (!xr_result(mInstance, result, "Getting number of reference spaces failed!"))
783 return;
784
785 std::vector<XrReferenceSpaceType> ref_spaces(ref_space_count);
786 result = xrEnumerateReferenceSpaces(mSession, ref_space_count, &ref_space_count, ref_spaces.data());
787 if (!xr_result(mInstance, result, "Enumerating reference spaces failed!"))
788 return;
789
790 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime supports %d reference spaces:\n", ref_space_count);
791 for (uint32_t i = 0; i < ref_space_count; i++) {
792 if (ref_spaces[i] == XR_REFERENCE_SPACE_TYPE_LOCAL) {
793 MMechostr(MSKRUNTIME, "\tXR_REFERENCE_SPACE_TYPE_LOCAL\n");
794 }
795 else if (ref_spaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) {
796 MMechostr(MSKRUNTIME, "\tXR_REFERENCE_SPACE_TYPE_STAGE\n");
797 }
798 else if (ref_spaces[i] == XR_REFERENCE_SPACE_TYPE_VIEW) {
799 MMechostr(MSKRUNTIME, "\tXR_REFERENCE_SPACE_TYPE_VIEW\n");
800 }
801 else {
802 MMechostr(MSKRUNTIME, "\tOther (extension?) refspace %u\\n", ref_spaces[i]);
803 }
804 }
805}
806
807
808sOpenXr* sOpenXr::CreateInstance(RenderSystem rsys, float scale)
809{
810 if (NULL == _singleton)
811 {
812 _singleton = new sOpenXr(rsys, scale);
813 }
814 else
815 {
816 _singleton->SetRenderer(rsys);
817 _singleton->SetScaleRatio(scale);
818 }
819
820 return _singleton;
821}
822
823sOpenXr* sOpenXr::GetInstance()
824{
825 if (NULL == _singleton)
826 _singleton = new sOpenXr();
827
828 return _singleton;
829}
830
831void sOpenXr::Kill()
832{
833 SAFE_DELETE(_singleton);
834}
835
836RenderSystem sOpenXr::GetRenderer()
837{
838 return mRendererType;
839}
840
841void sOpenXr::SetScaleRatio(float ratio)
842{
843 if (mScaleRatio != ratio)
844 {
845 mScaleRatio = ratio;
846 if (mConnected)
847 {
848 Disconnect();
849 Connect();
850 }
851 }
852}
853
854XrInstance sOpenXr::GetXrInstance()
855{
856 return mInstance;
857}
858
859XrSession sOpenXr::GetXrSession()
860{
861 return mSession;
862}
863
864bool sOpenXr::IsConnected()
865{
866 return mConnected;
867}
868
869bool sOpenXr::Connect()
870{
871 if (mConnected)
872 return false;
873
874 mConnected = initialize();
875
876 if (!mConnected)
877 return false;
878
879 return true;
880}
881
882std::string sOpenXr::GetHmdName()
883{
884 if (!mConnected)
885 return "";
886
887 return mDeviceName;
888}
889
890void sOpenXr::Disconnect()
891{
892 if (mConnected)
893 {
894 mConnected = false;
895 }
896
897 cleanup();
898}
899
900void sOpenXr::SetState(bool state)
901{
902 mPaused = !state;
903
904 //hack to restore passthrough after a system pause
905 if (mPassthroughEnable)
906 {
907 mPassthroughEnable = false;
908 updatePassthrough();
909 mPassthroughEnable = true;
910 updatePassthrough();
911 }
912}
913
914bool sOpenXr::IsVisible()
915{
916 return (!mPaused && (mState == XR_SESSION_STATE_VISIBLE || mState == XR_SESSION_STATE_FOCUSED)) ? true : false;
917}
918
919bool sOpenXr::GetHmdOrientation(Quaternion &quat)
920{
921 if (!mConnected || !mPoseValid)
922 return false;
923
924 quat = mPreviousHmdQuat;
925 return true;
926}
927
928bool sOpenXr::GetHmdPosition(Vector3 &vec)
929{
930 if (!mConnected || !mPoseValid)
931 return false;
932
933 vec = mPreviousHmdPos;
934 return true;
935}
936
937bool sOpenXr::GetProjectionMatrix(VrEye eye, float nearclip, float farclip, Matrix4 &mat)
938{
939 if (!mConnected || !mPoseValid)
940 return false;
941
942 XrMatrix4x4f proj;
943 XrMatrix4x4f_CreateProjectionFov(&proj, GRAPHICS_OPENGL, mViews[eye].fov, nearclip, farclip);
944 mat = Matrix4(proj);
945 return true;
946}
947
948bool sOpenXr::GetStereoTextureSize(unsigned int &w, unsigned int &h)
949{
950 if (!mConnected)
951 return false;
952
953 // scale a bit for better quality
954 w = (unsigned int)(mViewconfigViews[1].recommendedImageRectWidth * mScaleRatio);
955 h = (unsigned int)(mViewconfigViews[1].recommendedImageRectHeight * mScaleRatio);
956
957 return true;
958}
959
960float sOpenXr::GetStereoConfigFovY()
961{
962 if (!mConnected || !mPoseValid)
963 return vrRadiansFromDegrees(90.0f);
964
965 float fovX = 0.0f;
966 float fovY = 0.0f;
967
968 XrFovInRadians(fovX, fovY, GRAPHICS_OPENGL, mViews[0].fov);
969
970 return fovY;
971}
972
973float sOpenXr::GetStereoConfigIPD()
974{
975 return mLastIPD;
976}
977
978XrTime sOpenXr::GetPredictedDisplayTime()
979{
980 return mPredictedDisplayTime;
981}
982
983bool sOpenXr::processEvents()
984{
985 XrEventDataBuffer runtime_event = { XR_TYPE_EVENT_DATA_BUFFER, XR_NULL_HANDLE };
986 XrResult poll_result = xrPollEvent(mInstance, &runtime_event);
987 XrResult result;
988
989 while (poll_result == XR_SUCCESS)
990 {
991 switch (runtime_event.type)
992 {
993 case XR_TYPE_EVENT_DATA_EVENTS_LOST:
994 {
995 XrEventDataEventsLost* event = (XrEventDataEventsLost*)&runtime_event;
996 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: %d events data lost!\n", event->lostEventCount);
997 // do we care if the runtime loses events?
998 break;
999 }
1000 case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING:
1001 {
1002 XrEventDataInstanceLossPending* event = (XrEventDataInstanceLossPending*)&runtime_event;
1003 mRequestRestart = true;
1004 mSessionRunning = false;
1005 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: instance loss pending at %lu! Destroying instance.\n", event->lossTime);
1006 break;
1007 }
1008 case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED:
1009 {
1010 XrEventDataSessionStateChanged* event = (XrEventDataSessionStateChanged*)&runtime_event;
1011 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: session state changed from %d to %d\n", mState, event->state);
1012
1013 const XrSessionState oldState = mState;
1014 mState = event->state;
1015
1016 if ((event->session != XR_NULL_HANDLE) && (event->session != mSession))
1017 break;
1018
1019 switch (mState)
1020 {
1021 case XR_SESSION_STATE_READY:
1022 {
1023 if (mSession != XR_NULL_HANDLE)
1024 {
1025 XrSessionBeginInfo session_begin_info = { XR_TYPE_SESSION_BEGIN_INFO, XR_NULL_HANDLE, mViewType };
1026 result = xrBeginSession(mSession, &session_begin_info);
1027 if (!xr_result(mInstance, result, "Failed to begin session!"))
1028 return false;
1029
1030 mSessionRunning = true;
1031 MMechostr(MSKRUNTIME, "[OPENXR]: Session started!\n");
1032 }
1033 break;
1034 }
1035 case XR_SESSION_STATE_STOPPING:
1036 {
1037 if (mSession != XR_NULL_HANDLE)
1038 {
1039 mSessionRunning = false;
1040 xrEndSession(mSession);
1041 }
1042 break;
1043 }
1044 case XR_SESSION_STATE_EXITING:
1045 {
1046 mRequestRestart = false;
1047 break;
1048 }
1049 case XR_SESSION_STATE_LOSS_PENDING:
1050 {
1051 // Poll for a new instance.
1052 mRequestRestart = true;
1053 break;
1054 }
1055 default:
1056 break;
1057 }
1058 break;
1059 }
1060 case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING:
1061 {
1062 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: reference space change pending!\n");
1063 XrEventDataReferenceSpaceChangePending* event = (XrEventDataReferenceSpaceChangePending*)&runtime_event;
1064 (void)event;
1065 // TODO: do something
1066 break;
1067 }
1068 case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED:
1069 {
1070 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: interaction profile changed!\n");
1071 XrEventDataInteractionProfileChanged* event = (XrEventDataInteractionProfileChanged*)&runtime_event;
1072 (void)event;
1073
1074 XrInteractionProfileState state = { XR_TYPE_INTERACTION_PROFILE_STATE };
1075
1076 for (int i = 0; i < MAX_VR_CONTROLLERS; i++)
1077 {
1078 XrResult res = xrGetCurrentInteractionProfile(mSession, mInput.handSubactionPath[i], &state);
1079 if (!xr_result(mInstance, res, "Failed to get interaction profile for %d", i))
1080 continue;
1081
1082 if (state.interactionProfile != XR_NULL_PATH)
1083 {
1084 uint32_t strl;
1085 char profile_str[XR_MAX_PATH_LENGTH];
1086 res = xrPathToString(mInstance, state.interactionProfile, XR_MAX_PATH_LENGTH, &strl, profile_str);
1087 if (!xr_result(mInstance, res, "Failed to get interaction profile path str"))
1088 continue;
1089
1090 if (std::string(profile_str) == "/interaction_profiles/khr/simple_controller")
1091 mLastControllerType = ControllerType::CLASSIC_CONTROLLER;
1092 else if (std::string(profile_str) == "/interaction_profiles/htc/vive_controller")
1093 mLastControllerType = ControllerType::HTCVIVE_CONTROLLER;
1094 else if (std::string(profile_str) == "/interaction_profiles/valve/index_controller")
1095 mLastControllerType = ControllerType::VALVE_INDEX_CONTROLLER;
1096 else if (std::string(profile_str) == "/interaction_profiles/bytedance/pico4_controller")
1097 mLastControllerType = ControllerType::PICO_PICO4_CONTROLLER;
1098 else if ((std::string(profile_str) == "/interaction_profiles/pico/neo3_controller") || (std::string(profile_str) == "/interaction_profiles/bytedance/pico_neo3_controller") || mIsPico)
1099 mLastControllerType = ControllerType::PICO_NEO3_CONTROLLER;
1100 else if (std::string(profile_str) == "/interaction_profiles/oculus/touch_controller")
1101 mLastControllerType = ControllerType::OCULUS_TOUCH_CONTROLLER;
1102 else if (std::string(profile_str) == "/interaction_profiles/microsoft/motion_controller")
1103 mLastControllerType = ControllerType::MS_MOTION_CONTROLLER;
1104 else
1105 mLastControllerType = ControllerType::CLASSIC_CONTROLLER;
1106
1107 MMechostr(MSKDEBUG, "[OPENXR]: Event: Interaction profile changed to %s\n", profile_str);
1108 }
1109 }
1110 break;
1111 }
1112
1113 case XR_TYPE_EVENT_DATA_VISIBILITY_MASK_CHANGED_KHR:
1114 {
1115 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: visibility mask changed!!\n");
1116 XrEventDataVisibilityMaskChangedKHR* event = (XrEventDataVisibilityMaskChangedKHR*)&runtime_event;
1117 (void)event;
1118 // this event is from an extension
1119 break;
1120 }
1121 case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT:
1122 {
1123 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: perf settings!\n");
1124 XrEventDataPerfSettingsEXT* event = (XrEventDataPerfSettingsEXT*)&runtime_event;
1125 (void)event;
1126 // this event is from an extension
1127 break;
1128 }
1129 case XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB:
1130 {
1131 MMechostr(MSKDEBUG, "[OPENXR]: EVENT: passthroughFB state!\n");
1132 XrEventDataPassthroughStateChangedFB* event = (XrEventDataPassthroughStateChangedFB *)&runtime_event;
1133 if (event->flags & XR_PASSTHROUGH_STATE_CHANGED_REINIT_REQUIRED_BIT_FB )
1134 {
1135 cleanPassthrough();
1136 createPassthrough();
1137 mPassthroughFB.available = true;
1138 updatePassthrough();
1139 }
1140 else if (event->flags & XR_PASSTHROUGH_STATE_CHANGED_RESTORED_ERROR_BIT_FB)
1141 {
1142 mPassthroughFB.available = true;
1143 updatePassthrough();
1144 }
1145 else if (event->flags & XR_PASSTHROUGH_STATE_CHANGED_NON_RECOVERABLE_ERROR_BIT_FB || event->flags & XR_PASSTHROUGH_STATE_CHANGED_RECOVERABLE_ERROR_BIT_FB)
1146 {
1147 mPassthroughFB.available = false;
1148 }
1149 (void)event;
1150 break;
1151 }
1152 default: MMechostr(MSKDEBUG, "[OPENXR]: Unhandled event type %d\n", runtime_event.type);
1153 }
1154
1155 runtime_event.type = XR_TYPE_EVENT_DATA_BUFFER;
1156 poll_result = xrPollEvent(mInstance, &runtime_event);
1157 }
1158
1159 if (poll_result == XR_EVENT_UNAVAILABLE)
1160 {
1161 // processed all events in the queue
1162 return true;
1163 }
1164 else
1165 {
1166 MMechostr(MSKRUNTIME, "[OPENXR]: Failed to poll events!\n");
1167 return false;
1168 }
1169}
1170
1171void sOpenXr::processActions()
1172{
1173 if (mState != XR_SESSION_STATE_FOCUSED)
1174 return;
1175
1176 XrResult result;
1177
1178 // Sync actions
1179 const XrActiveActionSet activeActionSet{ mInput.actionSet, XR_NULL_PATH };
1180 XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO, XR_NULL_HANDLE, 1, &activeActionSet };
1181 result = xrSyncActions(mSession, &syncInfo);
1182 xr_result(mInstance, result, "failed to sync actions!");
1183
1184 // Get pose and grab action state and start haptic vibrate when hand is 90% squeezed.
1185 for (auto hand : { VrController::Left, VrController::Right })
1186 {
1187 XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
1188 getInfo.subactionPath = mInput.handSubactionPath[hand];
1189
1190 unsigned int iact = 0;
1191 for (auto act : mInput.buttonAction)
1192 {
1193 if (act != 0)
1194 {
1195 getInfo.action = act;
1196 XrActionStateBoolean btnState{ XR_TYPE_ACTION_STATE_BOOLEAN };
1197 btnState.currentState = XR_FALSE;
1198 result = xrGetActionStateBoolean(mSession, &getInfo, &btnState);
1199 mInput.buttonStates[hand][iact] = btnState.currentState == XR_TRUE ? true : false;
1200 }
1201 iact++;
1202 }
1203
1204 iact = 0;
1205 for (auto act : mInput.analogAction)
1206 {
1207 if (act != 0)
1208 {
1209 getInfo.action = act;
1210 XrActionStateFloat analogState{ XR_TYPE_ACTION_STATE_FLOAT };
1211 analogState.currentState = 0.0;
1212 result = xrGetActionStateFloat(mSession, &getInfo, &analogState);
1213
1214 // send axis when button is clicked for HTC vive
1215 if ((mLastControllerType == HTCVIVE_CONTROLLER) && ((iact == analogPadX) || (iact == analogPadY)) && !mInput.buttonStates[hand][(hand == VrController::Left) ? btnLThumb : btnRThumb])
1216 mInput.analogStates[hand][iact] = 0.0;
1217 else
1218 mInput.analogStates[hand][iact] = analogState.currentState;
1219 }
1220 iact++;
1221 }
1222
1223 iact = 0;
1224 for (auto act : mInput.touchAction)
1225 {
1226 if (act != 0)
1227 {
1228 getInfo.action = act;
1229 XrActionStateBoolean touchState{ XR_TYPE_ACTION_STATE_BOOLEAN };
1230 touchState.currentState = XR_FALSE;
1231 result = xrGetActionStateBoolean(mSession, &getInfo, &touchState);
1232
1233 mInput.touchStates[hand][iact] = touchState.currentState == XR_TRUE ? true : false;
1234 }
1235
1236 iact++;
1237 }
1238
1239 //manage gestures from combinations
1240 if (hand == VrController::Left)
1241 mInput.touchStates[hand][ControllerTouch::touchThumbUp] = (!mInput.touchStates[hand][ControllerTouch::touchThumbUp] &&
1242 !mInput.touchStates[hand][ControllerTouch::touchLThumb] &&
1243 !mInput.touchStates[hand][ControllerTouch::touchX] &&
1244 !mInput.touchStates[hand][ControllerTouch::touchY]
1245 ) ? true : false;
1246 else
1247 mInput.touchStates[hand][ControllerTouch::touchThumbUp] = (!mInput.touchStates[hand][ControllerTouch::touchThumbUp] &&
1248 !mInput.touchStates[hand][ControllerTouch::touchRThumb] &&
1249 !mInput.touchStates[hand][ControllerTouch::touchA] &&
1250 !mInput.touchStates[hand][ControllerTouch::touchB]
1251 ) ? true : false;
1252
1253 //Index pointing
1254 if (mLastControllerType == HTCVIVE_CONTROLLER)
1255 mInput.touchStates[hand][ControllerTouch::touchIndexPointing] = (mInput.analogStates[hand][ControllerAnalog::analogTrigger] == 0.0f && mInput.analogStates[hand][ControllerAnalog::analogGrip] > 0.0f) ? true : false;
1256 else
1257 mInput.touchStates[hand][ControllerTouch::touchIndexPointing] = (!mInput.touchStates[hand][ControllerTouch::touchTrigger] && mInput.analogStates[hand][ControllerAnalog::analogGrip] > 0.0f) ? true : false;
1258
1259 getInfo.action = mInput.poseAction;
1260 XrActionStatePose poseState{ XR_TYPE_ACTION_STATE_POSE };
1261 result = xrGetActionStatePose(mSession, &getInfo, &poseState);
1262 mInput.handActive[hand] = poseState.isActive;
1263 }
1264
1265}
1266
1267bool sOpenXr::initControllers()
1268{
1269 XrResult result;
1270 // Create an action set.
1271 {
1272 XrActionSetCreateInfo actionSetInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
1273 strcpy_s(actionSetInfo.actionSetName, "gameplay");
1274 strcpy_s(actionSetInfo.localizedActionSetName, "Gameplay");
1275 actionSetInfo.priority = 0;
1276 result = xrCreateActionSet(mInstance, &actionSetInfo, &mInput.actionSet);
1277 if (!xr_result(mInstance, result, "failed to creates actionset!"))
1278 return false;
1279 }
1280
1281 // Get the XrPath for the left and right hands - we will use them as subaction paths.
1282 xrStringToPath(mInstance, "/user/hand/left", &mInput.handSubactionPath[VrController::Left]);
1283 xrStringToPath(mInstance, "/user/hand/right", &mInput.handSubactionPath[VrController::Right]);
1284
1285 mInput.handVisible[VrController::Left] = false;
1286 mInput.handVisible[VrController::Right] = false;
1287
1288 mInput.buttonAction.resize(ControllerButtons::btnSize, XrAction{ XR_NULL_HANDLE });
1289 mInput.touchAction.resize(ControllerTouch::touchSize, XrAction{ XR_NULL_HANDLE });
1290 mInput.analogAction.resize(ControllerAnalog::analogSize, XrAction{ XR_NULL_HANDLE });
1291
1292 mInput.buttonStates.resize(MAX_VR_CONTROLLERS);
1293 mInput.touchStates.resize(MAX_VR_CONTROLLERS);
1294 mInput.analogStates.resize(MAX_VR_CONTROLLERS);
1295 for (auto hand : { VrController::Left, VrController::Right })
1296 {
1297 mInput.buttonStates[hand].resize(mInput.buttonAction.size(), false);
1298 mInput.touchStates[hand].resize(mInput.touchAction.size(), false);
1299 mInput.analogStates[hand].resize(mInput.analogAction.size(), 0.0f);
1300 }
1301
1302 // Create actions.
1303 // Create an input action for grabbing objects with the left and right hands.
1304 XrActionCreateInfo actionInfo{ XR_TYPE_ACTION_CREATE_INFO };
1305 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1306 strcpy_s(actionInfo.actionName, "a_button");
1307 strcpy_s(actionInfo.localizedActionName, "A Button");
1308 actionInfo.countSubactionPaths = 1;
1309 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Right];
1310 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnA]);
1311 if (!xr_result(mInstance, result, "failed to creates A Button action!"))
1312 return false;
1313
1314 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1315 strcpy_s(actionInfo.actionName, "a_touch");
1316 strcpy_s(actionInfo.localizedActionName, "A Touch");
1317 actionInfo.countSubactionPaths = 1;
1318 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Right];
1319 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchA]);
1320 if (!xr_result(mInstance, result, "failed to creates A Touch action!"))
1321 return false;
1322
1323 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1324 strcpy_s(actionInfo.actionName, "b_button");
1325 strcpy_s(actionInfo.localizedActionName, "B Button");
1326 actionInfo.countSubactionPaths = 1;
1327 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Right];
1328 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnB]);
1329 if (!xr_result(mInstance, result, "failed to creates B Button action!"))
1330 return false;
1331
1332 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1333 strcpy_s(actionInfo.actionName, "b_touch");
1334 strcpy_s(actionInfo.localizedActionName, "B Touch");
1335 actionInfo.countSubactionPaths = 1;
1336 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Right];
1337 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchB]);
1338 if (!xr_result(mInstance, result, "failed to creates B Touch action!"))
1339 return false;
1340
1341 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1342 strcpy_s(actionInfo.actionName, "x_button");
1343 strcpy_s(actionInfo.localizedActionName, "X Button");
1344 actionInfo.countSubactionPaths = 1;
1345 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Left];
1346 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnX]);
1347 if (!xr_result(mInstance, result, "failed to creates X Button action!"))
1348 return false;
1349
1350 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1351 strcpy_s(actionInfo.actionName, "x_touch");
1352 strcpy_s(actionInfo.localizedActionName, "X Touch");
1353 actionInfo.countSubactionPaths = 1;
1354 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Left];
1355 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchX]);
1356 if (!xr_result(mInstance, result, "failed to creates X Touch action!"))
1357 return false;
1358
1359 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1360 strcpy_s(actionInfo.actionName, "y_button");
1361 strcpy_s(actionInfo.localizedActionName, "Y Button");
1362 actionInfo.countSubactionPaths = 1;
1363 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Left];
1364 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnY]);
1365 if (!xr_result(mInstance, result, "failed to creates Y Button action!"))
1366 return false;
1367
1368 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1369 strcpy_s(actionInfo.actionName, "y_touch");
1370 strcpy_s(actionInfo.localizedActionName, "Y Touch");
1371 actionInfo.countSubactionPaths = 1;
1372 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Left];
1373 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchY]);
1374 if (!xr_result(mInstance, result, "failed to creates Y Touch action!"))
1375 return false;
1376
1377 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1378 strcpy_s(actionInfo.actionName, "rthumb_button");
1379 strcpy_s(actionInfo.localizedActionName, "Right Thumb Button");
1380 actionInfo.countSubactionPaths = 1;
1381 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Right];
1382 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnRThumb]);
1383 if (!xr_result(mInstance, result, "failed to creates Right Thumb Button action!"))
1384 return false;
1385
1386 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1387 strcpy_s(actionInfo.actionName, "rthumb_touch");
1388 strcpy_s(actionInfo.localizedActionName, "Right Thumb Touch");
1389 actionInfo.countSubactionPaths = 1;
1390 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Right];
1391 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchRThumb]);
1392 if (!xr_result(mInstance, result, "failed to creates Right Thumb Button action!"))
1393 return false;
1394
1395 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1396 strcpy_s(actionInfo.actionName, "lthumb_button");
1397 strcpy_s(actionInfo.localizedActionName, "Left Thumb Button");
1398 actionInfo.countSubactionPaths = 1;
1399 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Left];
1400 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnLThumb]);
1401 if (!xr_result(mInstance, result, "failed to creates Left Thumb action!"))
1402 return false;
1403
1404 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1405 strcpy_s(actionInfo.actionName, "lthumb_touch");
1406 strcpy_s(actionInfo.localizedActionName, "Left Thumb Touch");
1407 actionInfo.countSubactionPaths = 1;
1408 actionInfo.subactionPaths = &mInput.handSubactionPath[VrController::Left];
1409 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchLThumb]);
1410 if (!xr_result(mInstance, result, "failed to creates Left Thumb action!"))
1411 return false;
1412
1413 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1414 strcpy_s(actionInfo.actionName, "thumbup_touch");
1415 strcpy_s(actionInfo.localizedActionName, "Thumbup Touch");
1416 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1417 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1418 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchThumbUp]);
1419 if (!xr_result(mInstance, result, "failed to creates Thumbup Touch action!"))
1420 return false;
1421
1422 actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
1423 strcpy_s(actionInfo.actionName, "thumbstick_x");
1424 strcpy_s(actionInfo.localizedActionName, "Thumbstick X value");
1425 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1426 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1427 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.analogAction[ControllerAnalog::analogPadX]);
1428 if (!xr_result(mInstance, result, "failed to creates Thumbstick X action!"))
1429 return false;
1430
1431 actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
1432 strcpy_s(actionInfo.actionName, "thumbstick_y");
1433 strcpy_s(actionInfo.localizedActionName, "Thumbstick Y value");
1434 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1435 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1436 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.analogAction[ControllerAnalog::analogPadY]);
1437 if (!xr_result(mInstance, result, "failed to creates Thumbstick Y action!"))
1438 return false;
1439
1440 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1441 strcpy_s(actionInfo.actionName, "grip_button");
1442 strcpy_s(actionInfo.localizedActionName, "Grip Button");
1443 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1444 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1445 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnGrip]);
1446 if (!xr_result(mInstance, result, "failed to creates Grip Button action!"))
1447 return false;
1448
1449 actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
1450 strcpy_s(actionInfo.actionName, "grip_value");
1451 strcpy_s(actionInfo.localizedActionName, "Grip Value");
1452 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1453 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1454 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.analogAction[ControllerAnalog::analogGrip]);
1455 if (!xr_result(mInstance, result, "failed to creates Grip Value action!"))
1456 return false;
1457
1458 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1459 strcpy_s(actionInfo.actionName, "trigger_button");
1460 strcpy_s(actionInfo.localizedActionName, "Trigger Button");
1461 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1462 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1463 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnTrigger]);
1464 if (!xr_result(mInstance, result, "failed to creates Trigger Button action!"))
1465 return false;
1466
1467 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1468 strcpy_s(actionInfo.actionName, "trigger_touch");
1469 strcpy_s(actionInfo.localizedActionName, "Trigger Touch");
1470 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1471 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1472 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.touchAction[ControllerTouch::touchTrigger]);
1473 if (!xr_result(mInstance, result, "failed to creates Trigger Touch action!"))
1474 return false;
1475
1476 actionInfo.actionType = XR_ACTION_TYPE_FLOAT_INPUT;
1477 strcpy_s(actionInfo.actionName, "trigger_value");
1478 strcpy_s(actionInfo.localizedActionName, "Trigger Value");
1479 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1480 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1481 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.analogAction[ControllerAnalog::analogTrigger]);
1482 if (!xr_result(mInstance, result, "failed to creates Trigger Value action!"))
1483 return false;
1484
1485 actionInfo.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
1486 strcpy_s(actionInfo.actionName, "menu_button");
1487 strcpy_s(actionInfo.localizedActionName, "Menu Button");
1488 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1489 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1490 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.buttonAction[ControllerButtons::btnMenu]);
1491 if (!xr_result(mInstance, result, "failed to creates Menu Button action!"))
1492 return false;
1493
1494 // Create an input action getting the left and right hand poses.
1495 actionInfo.actionType = XR_ACTION_TYPE_POSE_INPUT;
1496 strcpy_s(actionInfo.actionName, "hand_pose");
1497 strcpy_s(actionInfo.localizedActionName, "Hand Pose");
1498 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1499 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1500 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.poseAction);
1501 if (!xr_result(mInstance, result, "failed to creates pose action!"))
1502 return false;
1503
1504 // Create output actions for vibrating the left and right controller.
1505 actionInfo.actionType = XR_ACTION_TYPE_VIBRATION_OUTPUT;
1506 strcpy_s(actionInfo.actionName, "vibrate_hand");
1507 strcpy_s(actionInfo.localizedActionName, "Vibrate Hand");
1508 actionInfo.countSubactionPaths = uint32_t(mInput.handSubactionPath.size());
1509 actionInfo.subactionPaths = mInput.handSubactionPath.data();
1510 result = xrCreateAction(mInput.actionSet, &actionInfo, &mInput.vibrateAction);
1511 if (!xr_result(mInstance, result, "failed to creates vibrate action!"))
1512 return false;
1513
1514 XrPath aClickPath;
1515 XrPath aTouchPath;
1516 XrPath bClickPath;
1517 XrPath bTouchPath;
1518
1519 XrPath xClickPath;
1520 XrPath xTouchPath;
1521 XrPath yClickPath;
1522 XrPath yTouchPath;
1523
1524 std::array<XrPath, MAX_VR_CONTROLLERS> thumbClickPath;
1525 std::array<XrPath, MAX_VR_CONTROLLERS> thumbTouchPath;
1526 std::array<XrPath, MAX_VR_CONTROLLERS> thumbRestPath;
1527
1528 std::array<XrPath, MAX_VR_CONTROLLERS> squeezeValuePath;
1529 std::array<XrPath, MAX_VR_CONTROLLERS> squeezeClickPath;
1530 std::array<XrPath, MAX_VR_CONTROLLERS> triggerValuePath;
1531 std::array<XrPath, MAX_VR_CONTROLLERS> triggerTouchPath;
1532 std::array<XrPath, MAX_VR_CONTROLLERS> triggerClickPath;
1533
1534 std::array<XrPath, MAX_VR_CONTROLLERS> trackpadXPath;
1535 std::array<XrPath, MAX_VR_CONTROLLERS> trackpadYPath;
1536 std::array<XrPath, MAX_VR_CONTROLLERS> trackpadTouchPath;
1537 std::array<XrPath, MAX_VR_CONTROLLERS> trackpadClickPath;
1538
1539 std::array<XrPath, MAX_VR_CONTROLLERS> thumbStickXPath;
1540 std::array<XrPath, MAX_VR_CONTROLLERS> thumbStickYPath;
1541
1542 std::array<XrPath, MAX_VR_CONTROLLERS> selectClickPath;
1543 std::array<XrPath, MAX_VR_CONTROLLERS> menuClickPath;
1544 std::array<XrPath, MAX_VR_CONTROLLERS> sysClickPath;
1545 std::array<XrPath, MAX_VR_CONTROLLERS> posePath;
1546 std::array<XrPath, MAX_VR_CONTROLLERS> hapticPath;
1547
1548 xrStringToPath(mInstance, "/user/hand/right/input/a/click", &aClickPath);
1549 xrStringToPath(mInstance, "/user/hand/right/input/a/touch", &aTouchPath);
1550 xrStringToPath(mInstance, "/user/hand/right/input/b/click", &bClickPath);
1551 xrStringToPath(mInstance, "/user/hand/right/input/b/touch", &bTouchPath);
1552
1553 xrStringToPath(mInstance, "/user/hand/left/input/x/click", &xClickPath);
1554 xrStringToPath(mInstance, "/user/hand/left/input/x/touch", &xTouchPath);
1555 xrStringToPath(mInstance, "/user/hand/left/input/y/click", &yClickPath);
1556 xrStringToPath(mInstance, "/user/hand/left/input/y/touch", &yTouchPath);
1557
1558 xrStringToPath(mInstance, "/user/hand/left/input/thumbstick/click", &thumbClickPath[VrController::Left]);
1559 xrStringToPath(mInstance, "/user/hand/right/input/thumbstick/click", &thumbClickPath[VrController::Right]);
1560 xrStringToPath(mInstance, "/user/hand/left/input/thumbstick/touch", &thumbTouchPath[VrController::Left]);
1561 xrStringToPath(mInstance, "/user/hand/right/input/thumbstick/touch", &thumbTouchPath[VrController::Right]);
1562
1563 xrStringToPath(mInstance, "/user/hand/left/input/thumbstick/x", &thumbStickXPath[VrController::Left]);
1564 xrStringToPath(mInstance, "/user/hand/right/input/thumbstick/x", &thumbStickXPath[VrController::Right]);
1565 xrStringToPath(mInstance, "/user/hand/left/input/thumbstick/y", &thumbStickYPath[VrController::Left]);
1566 xrStringToPath(mInstance, "/user/hand/right/input/thumbstick/y", &thumbStickYPath[VrController::Right]);
1567
1568 xrStringToPath(mInstance, "/user/hand/left/input/thumbrest/touch", &thumbRestPath[VrController::Left]);
1569 xrStringToPath(mInstance, "/user/hand/right/input/thumbrest/touch", &thumbRestPath[VrController::Right]);
1570
1571 xrStringToPath(mInstance, "/user/hand/left/input/squeeze/value", &squeezeValuePath[VrController::Left]);
1572 xrStringToPath(mInstance, "/user/hand/right/input/squeeze/value", &squeezeValuePath[VrController::Right]);
1573 xrStringToPath(mInstance, "/user/hand/left/input/squeeze/click", &squeezeClickPath[VrController::Left]);
1574 xrStringToPath(mInstance, "/user/hand/right/input/squeeze/click", &squeezeClickPath[VrController::Right]);
1575
1576 xrStringToPath(mInstance, "/user/hand/left/input/trigger/value", &triggerValuePath[VrController::Left]);
1577 xrStringToPath(mInstance, "/user/hand/right/input/trigger/value", &triggerValuePath[VrController::Right]);
1578 xrStringToPath(mInstance, "/user/hand/left/input/trigger/touch", &triggerTouchPath[VrController::Left]);
1579 xrStringToPath(mInstance, "/user/hand/right/input/trigger/touch", &triggerTouchPath[VrController::Right]);
1580 xrStringToPath(mInstance, "/user/hand/left/input/trigger/click", &triggerClickPath[VrController::Left]);
1581 xrStringToPath(mInstance, "/user/hand/right/input/trigger/click", &triggerClickPath[VrController::Right]);
1582
1583 xrStringToPath(mInstance, "/user/hand/left/input/trackpad/x", &trackpadXPath[VrController::Left]);
1584 xrStringToPath(mInstance, "/user/hand/right/input/trackpad/x", &trackpadXPath[VrController::Right]);
1585 xrStringToPath(mInstance, "/user/hand/left/input/trackpad/y", &trackpadYPath[VrController::Left]);
1586 xrStringToPath(mInstance, "/user/hand/right/input/trackpad/y", &trackpadYPath[VrController::Right]);
1587 xrStringToPath(mInstance, "/user/hand/left/input/trackpad/touch", &trackpadTouchPath[VrController::Left]);
1588 xrStringToPath(mInstance, "/user/hand/right/input/trackpad/touch", &trackpadTouchPath[VrController::Right]);
1589 xrStringToPath(mInstance, "/user/hand/left/input/trackpad/click", &trackpadClickPath[VrController::Left]);
1590 xrStringToPath(mInstance, "/user/hand/right/input/trackpad/click", &trackpadClickPath[VrController::Right]);
1591
1592 xrStringToPath(mInstance, "/user/hand/left/input/select/click", &selectClickPath[VrController::Left]);
1593 xrStringToPath(mInstance, "/user/hand/right/input/select/click", &selectClickPath[VrController::Right]);
1594
1595 xrStringToPath(mInstance, "/user/hand/left/input/menu/click", &menuClickPath[VrController::Left]);
1596 xrStringToPath(mInstance, "/user/hand/right/input/menu/click", &menuClickPath[VrController::Right]);
1597
1598 xrStringToPath(mInstance, "/user/hand/left/input/system/click", &sysClickPath[VrController::Left]);
1599 xrStringToPath(mInstance, "/user/hand/right/input/system/click", &sysClickPath[VrController::Right]);
1600
1601 xrStringToPath(mInstance, "/user/hand/left/input/grip/pose", &posePath[VrController::Left]);
1602 xrStringToPath(mInstance, "/user/hand/right/input/grip/pose", &posePath[VrController::Right]);
1603 xrStringToPath(mInstance, "/user/hand/left/output/haptic", &hapticPath[VrController::Left]);
1604 xrStringToPath(mInstance, "/user/hand/right/output/haptic", &hapticPath[VrController::Right]);
1605
1606 // Suggest bindings for KHR Simple.
1607 {
1608 XrPath khrSimpleInteractionProfilePath;
1609 xrStringToPath(mInstance, "/interaction_profiles/khr/simple_controller", &khrSimpleInteractionProfilePath);
1610 std::vector<XrActionSuggestedBinding> bindings{ {
1611 {mInput.buttonAction[ControllerButtons::btnTrigger], selectClickPath[VrController::Left]},
1612 {mInput.buttonAction[ControllerButtons::btnTrigger], selectClickPath[VrController::Right]},
1613 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Left]},
1614 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Right]},
1615 {mInput.poseAction, posePath[VrController::Left]},
1616 {mInput.poseAction, posePath[VrController::Right]},
1617 {mInput.vibrateAction, hapticPath[VrController::Left]},
1618 {mInput.vibrateAction, hapticPath[VrController::Right]}} };
1619 XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
1620 suggestedBindings.interactionProfile = khrSimpleInteractionProfilePath;
1621 suggestedBindings.suggestedBindings = bindings.data();
1622 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
1623 result = xrSuggestInteractionProfileBindings(mInstance, &suggestedBindings);
1624 if (!xr_result(mInstance, result, "failed to suggest bindings"))
1625 return false;
1626 }
1627
1628 // Suggest bindings for the Pico Controller.
1629 if (mPicoController.supported)
1630 {
1631 {
1632 XrPath picoInteractionProfilePath;
1633 xrStringToPath(mInstance, "/interaction_profiles/bytedance/pico_neo3_controller", &picoInteractionProfilePath);
1634 std::vector<XrActionSuggestedBinding> bindings{ {
1635 {mInput.buttonAction[ControllerButtons::btnA], aClickPath},
1636 {mInput.touchAction[ControllerTouch::touchA], aTouchPath},
1637 {mInput.buttonAction[ControllerButtons::btnB], bClickPath},
1638 {mInput.touchAction[ControllerTouch::touchB], bTouchPath},
1639 {mInput.buttonAction[ControllerButtons::btnX], xClickPath},
1640 {mInput.touchAction[ControllerTouch::touchX], xTouchPath},
1641 {mInput.buttonAction[ControllerButtons::btnY], yClickPath},
1642 {mInput.touchAction[ControllerTouch::touchY], yTouchPath},
1643
1644 {mInput.buttonAction[ControllerButtons::btnRThumb], thumbClickPath[VrController::Right]},
1645 {mInput.buttonAction[ControllerButtons::btnLThumb], thumbClickPath[VrController::Left]},
1646 {mInput.touchAction[ControllerTouch::touchRThumb], thumbTouchPath[VrController::Right]},
1647 {mInput.touchAction[ControllerTouch::touchLThumb], thumbTouchPath[VrController::Left]},
1648 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Left]},
1649 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Right]},
1650 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Left]},
1651 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Right]},
1652
1653 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Left]},
1654 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Right]},
1655 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeValuePath[VrController::Left]},
1656 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeValuePath[VrController::Right]},
1657
1658 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Left]},
1659 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Right]},
1660 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Left]},
1661 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Right]},
1662 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Left]},
1663 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Right]},
1664
1665 {mInput.touchAction[ControllerTouch::touchThumbUp], thumbRestPath[VrController::Left]},
1666 {mInput.touchAction[ControllerTouch::touchThumbUp], thumbRestPath[VrController::Right]},
1667
1668 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Left]},
1669 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Right]},
1670 {mInput.poseAction, posePath[VrController::Left]},
1671 {mInput.poseAction, posePath[VrController::Right]},
1672 {mInput.vibrateAction, hapticPath[VrController::Left]},
1673 {mInput.vibrateAction, hapticPath[VrController::Right]}} };
1674
1675 XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
1676 suggestedBindings.interactionProfile = picoInteractionProfilePath;
1677 suggestedBindings.suggestedBindings = bindings.data();
1678 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
1679 result = xrSuggestInteractionProfileBindings(mInstance, &suggestedBindings);
1680 if (result == XR_ERROR_PATH_UNSUPPORTED)
1681 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime does not supports %s\n", "/interaction_profiles/pico/neo3_controller");
1682 else
1683 xr_result(mInstance, result, "failed to suggest bindings");
1684 }
1685
1686 {
1687 XrPath picoInteractionProfilePath;
1688 xrStringToPath(mInstance, "/interaction_profiles/bytedance/pico4_controller", &picoInteractionProfilePath);
1689 std::vector<XrActionSuggestedBinding> bindings{ {
1690 {mInput.buttonAction[ControllerButtons::btnA], aClickPath},
1691 {mInput.touchAction[ControllerTouch::touchA], aTouchPath},
1692 {mInput.buttonAction[ControllerButtons::btnB], bClickPath},
1693 {mInput.touchAction[ControllerTouch::touchB], bTouchPath},
1694 {mInput.buttonAction[ControllerButtons::btnX], xClickPath},
1695 {mInput.touchAction[ControllerTouch::touchX], xTouchPath},
1696 {mInput.buttonAction[ControllerButtons::btnY], yClickPath},
1697 {mInput.touchAction[ControllerTouch::touchY], yTouchPath},
1698
1699 {mInput.buttonAction[ControllerButtons::btnRThumb], thumbClickPath[VrController::Right]},
1700 {mInput.buttonAction[ControllerButtons::btnLThumb], thumbClickPath[VrController::Left]},
1701 {mInput.touchAction[ControllerTouch::touchRThumb], thumbTouchPath[VrController::Right]},
1702 {mInput.touchAction[ControllerTouch::touchLThumb], thumbTouchPath[VrController::Left]},
1703 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Left]},
1704 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Right]},
1705 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Left]},
1706 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Right]},
1707
1708 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Left]},
1709 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Right]},
1710 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeValuePath[VrController::Left]},
1711 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeValuePath[VrController::Right]},
1712
1713 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Left]},
1714 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Right]},
1715 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Left]},
1716 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Right]},
1717 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Left]},
1718 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Right]},
1719
1720 {mInput.touchAction[ControllerTouch::touchThumbUp], thumbRestPath[VrController::Left]},
1721 {mInput.touchAction[ControllerTouch::touchThumbUp], thumbRestPath[VrController::Right]},
1722
1723 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Left]},
1724 {mInput.poseAction, posePath[VrController::Left]},
1725 {mInput.poseAction, posePath[VrController::Right]},
1726 {mInput.vibrateAction, hapticPath[VrController::Left]},
1727 {mInput.vibrateAction, hapticPath[VrController::Right]}} };
1728
1729 XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
1730 suggestedBindings.interactionProfile = picoInteractionProfilePath;
1731 suggestedBindings.suggestedBindings = bindings.data();
1732 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
1733 result = xrSuggestInteractionProfileBindings(mInstance, &suggestedBindings);
1734 if (result == XR_ERROR_PATH_UNSUPPORTED)
1735 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime does not supports %s\n", "/interaction_profiles/bytedance/pico4_controller");
1736 else
1737 xr_result(mInstance, result, "failed to suggest bindings");
1738 }
1739 }
1740
1741 // Suggest bindings for the Oculus Touch.
1742 {
1743 XrPath oculusTouchInteractionProfilePath;
1744 xrStringToPath(mInstance, "/interaction_profiles/oculus/touch_controller", &oculusTouchInteractionProfilePath);
1745 std::vector<XrActionSuggestedBinding> bindings{ {
1746 {mInput.buttonAction[ControllerButtons::btnA], aClickPath},
1747 {mInput.touchAction[ControllerTouch::touchA], aTouchPath},
1748 {mInput.buttonAction[ControllerButtons::btnB], bClickPath},
1749 {mInput.touchAction[ControllerTouch::touchB], bTouchPath},
1750 {mInput.buttonAction[ControllerButtons::btnX], xClickPath},
1751 {mInput.touchAction[ControllerTouch::touchX], xTouchPath},
1752 {mInput.buttonAction[ControllerButtons::btnY], yClickPath},
1753 {mInput.touchAction[ControllerTouch::touchY], yTouchPath},
1754
1755 {mInput.buttonAction[ControllerButtons::btnRThumb], thumbClickPath[VrController::Right]},
1756 {mInput.buttonAction[ControllerButtons::btnLThumb], thumbClickPath[VrController::Left]},
1757 {mInput.touchAction[ControllerTouch::touchRThumb], thumbTouchPath[VrController::Right]},
1758 {mInput.touchAction[ControllerTouch::touchLThumb], thumbTouchPath[VrController::Left]},
1759 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Left]},
1760 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Right]},
1761 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Left]},
1762 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Right]},
1763
1764 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Left]},
1765 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Right]},
1766 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeValuePath[VrController::Left]},
1767 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeValuePath[VrController::Right]},
1768
1769 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Left]},
1770 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Right]},
1771 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerValuePath[VrController::Left]},
1772 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerValuePath[VrController::Right]},
1773 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Left]},
1774 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Right]},
1775
1776 {mInput.touchAction[ControllerTouch::touchThumbUp], thumbRestPath[VrController::Left]},
1777 {mInput.touchAction[ControllerTouch::touchThumbUp], thumbRestPath[VrController::Right]},
1778
1779 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Left]},
1780 {mInput.poseAction, posePath[VrController::Left]},
1781 {mInput.poseAction, posePath[VrController::Right]},
1782 {mInput.vibrateAction, hapticPath[VrController::Left]},
1783 {mInput.vibrateAction, hapticPath[VrController::Right]}} };
1784
1785 XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
1786 suggestedBindings.interactionProfile = oculusTouchInteractionProfilePath;
1787 suggestedBindings.suggestedBindings = bindings.data();
1788 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
1789 result = xrSuggestInteractionProfileBindings(mInstance, &suggestedBindings);
1790 if (result == XR_ERROR_PATH_UNSUPPORTED)
1791 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime does not supports %s\n", "/interaction_profiles/oculus/touch_controller");
1792 else
1793 xr_result(mInstance, result, "failed to suggest bindings");
1794 }
1795
1796 // Suggest bindings for the Vive Controller.
1797 {
1798 XrPath viveControllerInteractionProfilePath;
1799 xrStringToPath(mInstance, "/interaction_profiles/htc/vive_controller", &viveControllerInteractionProfilePath);
1800 std::vector<XrActionSuggestedBinding> bindings{ {
1801 {mInput.buttonAction[ControllerButtons::btnRThumb], trackpadClickPath[VrController::Right]},
1802 {mInput.buttonAction[ControllerButtons::btnLThumb], trackpadClickPath[VrController::Left]},
1803 {mInput.touchAction[ControllerTouch::touchRThumb], trackpadTouchPath[VrController::Right]},
1804 {mInput.touchAction[ControllerTouch::touchLThumb], trackpadTouchPath[VrController::Left]},
1805 {mInput.analogAction[ControllerAnalog::analogPadX], trackpadXPath[VrController::Left]},
1806 {mInput.analogAction[ControllerAnalog::analogPadX], trackpadXPath[VrController::Right]},
1807 {mInput.analogAction[ControllerAnalog::analogPadY], trackpadYPath[VrController::Left]},
1808 {mInput.analogAction[ControllerAnalog::analogPadY], trackpadYPath[VrController::Right]},
1809
1810 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeClickPath[VrController::Left]},
1811 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeClickPath[VrController::Right]},
1812 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeClickPath[VrController::Left]},
1813 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeClickPath[VrController::Right]},
1814
1815 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Left]},
1816 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Right]},
1817 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Left]},
1818 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Right]},
1819
1820 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Left]},
1821 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Right]},
1822 {mInput.poseAction, posePath[VrController::Left]},
1823 {mInput.poseAction, posePath[VrController::Right]},
1824 {mInput.vibrateAction, hapticPath[VrController::Left]},
1825 {mInput.vibrateAction, hapticPath[VrController::Right]}} };
1826 XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
1827 suggestedBindings.interactionProfile = viveControllerInteractionProfilePath;
1828 suggestedBindings.suggestedBindings = bindings.data();
1829 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
1830 result = xrSuggestInteractionProfileBindings(mInstance, &suggestedBindings);
1831 if (result == XR_ERROR_PATH_UNSUPPORTED)
1832 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime does not supports %s\n", "/interaction_profiles/htc/vive_controller");
1833 else
1834 xr_result(mInstance, result, "failed to suggest bindings");
1835 }
1836
1837 // Suggest bindings for the Valve Index Controller.
1838 {
1839 XrPath indexControllerInteractionProfilePath;
1840 xrStringToPath(mInstance, "/interaction_profiles/valve/index_controller", &indexControllerInteractionProfilePath);
1841 std::vector<XrActionSuggestedBinding> bindings{ {
1842 {mInput.buttonAction[ControllerButtons::btnRThumb], thumbClickPath[VrController::Right]},
1843 {mInput.buttonAction[ControllerButtons::btnLThumb], thumbClickPath[VrController::Left]},
1844 {mInput.touchAction[ControllerTouch::touchRThumb], thumbTouchPath[VrController::Right]},
1845 {mInput.touchAction[ControllerTouch::touchLThumb], thumbTouchPath[VrController::Left]},
1846 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Left]},
1847 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Right]},
1848 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Left]},
1849 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Right]},
1850
1851 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Left]},
1852 {mInput.analogAction[ControllerAnalog::analogGrip], squeezeValuePath[VrController::Right]},
1853
1854 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Left]},
1855 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Right]},
1856 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Left]},
1857 {mInput.touchAction[ControllerTouch::touchTrigger], triggerTouchPath[VrController::Right]},
1858 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Left]},
1859 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerClickPath[VrController::Right]},
1860
1861 {mInput.buttonAction[ControllerButtons::btnMenu], sysClickPath[VrController::Left]},
1862 {mInput.buttonAction[ControllerButtons::btnMenu], sysClickPath[VrController::Right]},
1863 {mInput.poseAction, posePath[VrController::Left]},
1864 {mInput.poseAction, posePath[VrController::Right]},
1865
1866 {mInput.vibrateAction, hapticPath[VrController::Left]},
1867 {mInput.vibrateAction, hapticPath[VrController::Right]}} };
1868 XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
1869 suggestedBindings.interactionProfile = indexControllerInteractionProfilePath;
1870 suggestedBindings.suggestedBindings = bindings.data();
1871 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
1872 result = xrSuggestInteractionProfileBindings(mInstance, &suggestedBindings);
1873 if (result == XR_ERROR_PATH_UNSUPPORTED)
1874 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime does not supports %s\n", "/interaction_profiles/valve/index_controller");
1875 else
1876 xr_result(mInstance, result, "failed to suggest bindings");
1877 }
1878
1879 // Suggest bindings for the Microsoft Mixed Reality Motion Controller.
1880 {
1881 XrPath microsoftMixedRealityInteractionProfilePath;
1882 xrStringToPath(mInstance, "/interaction_profiles/microsoft/motion_controller", &microsoftMixedRealityInteractionProfilePath);
1883 std::vector<XrActionSuggestedBinding> bindings{ {
1884 {mInput.buttonAction[ControllerButtons::btnRThumb], thumbClickPath[VrController::Right]},
1885 {mInput.buttonAction[ControllerButtons::btnLThumb], thumbClickPath[VrController::Left]},
1886 {mInput.touchAction[ControllerTouch::touchRThumb], trackpadTouchPath[VrController::Right]},
1887 {mInput.touchAction[ControllerTouch::touchLThumb], trackpadTouchPath[VrController::Left]},
1888 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Left]},
1889 {mInput.analogAction[ControllerAnalog::analogPadX], thumbStickXPath[VrController::Right]},
1890 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Left]},
1891 {mInput.analogAction[ControllerAnalog::analogPadY], thumbStickYPath[VrController::Right]},
1892
1893 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeClickPath[VrController::Left]},
1894 {mInput.buttonAction[ControllerButtons::btnGrip], squeezeClickPath[VrController::Right]},
1895
1896 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Left]},
1897 {mInput.analogAction[ControllerAnalog::analogTrigger], triggerValuePath[VrController::Right]},
1898 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerValuePath[VrController::Left]},
1899 {mInput.buttonAction[ControllerButtons::btnTrigger], triggerValuePath[VrController::Right]},
1900
1901 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Left]},
1902 {mInput.buttonAction[ControllerButtons::btnMenu], menuClickPath[VrController::Right]},
1903 {mInput.poseAction, posePath[VrController::Left]},
1904 {mInput.poseAction, posePath[VrController::Right]},
1905 {mInput.vibrateAction, hapticPath[VrController::Left]},
1906 {mInput.vibrateAction, hapticPath[VrController::Right]}} };
1907 XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
1908 suggestedBindings.interactionProfile = microsoftMixedRealityInteractionProfilePath;
1909 suggestedBindings.suggestedBindings = bindings.data();
1910 suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size();
1911 result = xrSuggestInteractionProfileBindings(mInstance, &suggestedBindings);
1912 if (result == XR_ERROR_PATH_UNSUPPORTED)
1913 MMechostr(MSKRUNTIME, "[OPENXR]: Runtime does not supports %s\n", "/interaction_profiles/microsoft/motion_controller");
1914 else
1915 xr_result(mInstance, result, "failed to suggest bindings");
1916 }
1917
1918 XrActionSpaceCreateInfo actionSpaceInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
1919 actionSpaceInfo.action = mInput.poseAction;
1920 actionSpaceInfo.poseInActionSpace.orientation.w = 1.f;
1921 actionSpaceInfo.subactionPath = mInput.handSubactionPath[VrController::Left];
1922 result = xrCreateActionSpace(mSession, &actionSpaceInfo, &mInput.handSpace[VrController::Left]);
1923 if (!xr_result(mInstance, result, "failed to create left hand pose space"))
1924 return false;
1925
1926 actionSpaceInfo.subactionPath = mInput.handSubactionPath[VrController::Right];
1927 result = xrCreateActionSpace(mSession, &actionSpaceInfo, &mInput.handSpace[VrController::Right]);
1928 if (!xr_result(mInstance, result, "failed to create right hand pose space"))
1929 return false;
1930
1931 XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO, XR_NULL_HANDLE, 1, &mInput.actionSet };
1932 result = xrAttachSessionActionSets(mSession, &attachInfo);
1933 if (!xr_result(mInstance, result, "failed to attach action set"))
1934 return false;
1935
1936 return true;
1937}
1938
1939void sOpenXr::getControllerOffset(ControllerType type, VrController side, bool skeletal, Vector3 &vec, Quaternion &quat)
1940{
1941 // offsets to match Oculus hands model wrist
1942 switch (type)
1943 {
1944 case HANDS_CONTROLLER:
1945 case CLASSIC_CONTROLLER:
1946 if (skeletal)
1947 quat = quat * Quaternion(sqrt(0.5f), 0.0f, 0.0f, (side == VrController::Right) ? sqrt(0.5f) : -sqrt(0.5f));
1948 break;
1949 case HTCVIVE_CONTROLLER:
1950 if (skeletal)
1951 {
1952 quat = quat * Quaternion(sqrt(0.5f), 0.0f, 0.0f, (side == VrController::Right) ? sqrt(0.5f) : -sqrt(0.5f)); //90° on Z
1953 quat = quat * Quaternion(0.9990482f, 0.0436194f, 0.0f, 0.0f); // 5° on X
1954 quat = quat * Quaternion(0.9961947f, 0.0f, (side == VrController::Right) ? -0.0871557f : 0.0871557f, 0.0f); // 10° on Y
1955 }
1956 else
1957 {
1958 quat = quat * Quaternion(0.9063078f, -0.4226183f, 0.0f, 0.0f); // -50° on X
1959 vec += quat * Vector3((side == VrController::Right) ? 0.04f : -0.04f, -0.005f, 0.08f);
1960 }
1961 break;
1962 case VALVE_INDEX_CONTROLLER:
1963 case PICO_PICO4_CONTROLLER:
1964 case PICO_NEO3_CONTROLLER:
1965 if (skeletal)
1966 {
1967 quat = quat * Quaternion(sqrt(0.5f), 0.0f, 0.0f, (side == VrController::Right) ? sqrt(0.5f) : -sqrt(0.5f));
1968 }
1969 else
1970 {
1971 quat = quat * Quaternion(0.9961947f, -0.0871557f, 0.0f, 0.0f); // 10° on X
1972 vec += quat * Vector3((side == VrController::Right) ? 0.06f : -0.06f, -0.03f, 0.09f);
1973 }
1974 break;
1975 case OCULUS_TOUCH_CONTROLLER:
1976 if (skeletal)
1977 {
1978 quat = quat * Quaternion(sqrt(0.5f), 0.0f, 0.0f, (side == VrController::Right) ? sqrt(0.5f) : -sqrt(0.5f));
1979 }
1980 else
1981 {
1982 quat = quat * Quaternion(0.8660254f, -0.5f, 0.0f, 0.0f); // -60° on X
1983 vec += quat * Vector3((side == VrController::Right) ? 0.04f : -0.04f, 0.0f, 0.06f);
1984 }
1985 break;
1986 case MS_MOTION_CONTROLLER:
1987 if (skeletal)
1988 quat = quat * ((side == VrController::Right) ? Quaternion(0.0f, 0.0f, 0.0f, 1.0f) * Quaternion(sqrt(0.5f), 0.0f, 0.0f, -sqrt(0.5f)) : Quaternion(sqrt(0.5f), 0.0f, 0.0f, sqrt(0.5f)));
1989 break;
1990 default:
1991 break;
1992 }
1993}
1994
1995void sOpenXr::updateControllers(XrFrameState frameState)
1996{
1997 XrResult result;
1998 XrHandJointLocationEXT joints[MAX_VR_CONTROLLERS][XR_HAND_JOINT_COUNT_EXT];
1999 XrHandJointVelocityEXT joint_velocity[MAX_VR_CONTROLLERS][XR_HAND_JOINT_COUNT_EXT];
2000 XrHandJointLocationsEXT joint_locations[MAX_VR_CONTROLLERS];
2001 XrHandJointVelocitiesEXT joint_velocities[MAX_VR_CONTROLLERS];
2002 XrHandTrackingAimStateFB aim_states[MAX_VR_CONTROLLERS];
2003
2004 for (auto hand : { VrController::Left, VrController::Right })
2005 {
2006 mInput.handVisible[hand] = false;
2007 if (mHandTracking.system_supported)
2008 {
2009 //MMechostr(MSKDEBUG, "[OPENXR]: Hand tracking Update\n");
2010 if (mHandTracking.gestures)
2011 aim_states[hand] = XrHandTrackingAimStateFB{ XR_TYPE_HAND_TRACKING_AIM_STATE_FB, XR_NULL_HANDLE };
2012
2013 joint_velocities[hand] = XrHandJointVelocitiesEXT{ XR_TYPE_HAND_JOINT_VELOCITIES_EXT, (mHandTracking.gestures) ? &aim_states[hand] : XR_NULL_HANDLE, XR_HAND_JOINT_COUNT_EXT, joint_velocity[hand] };
2014 joint_locations[hand] = XrHandJointLocationsEXT{ XR_TYPE_HAND_JOINT_LOCATIONS_EXT, &joint_velocities[hand], 0, XR_HAND_JOINT_COUNT_EXT, joints[hand] };
2015
2016 if (mHandTracking.trackers[hand] == XR_NULL_HANDLE)
2017 continue;
2018
2019 XrHandJointsLocateInfoEXT globalInfo = { XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT, XR_NULL_HANDLE, mPlaySpace, frameState.predictedDisplayTime };
2020 result = mHandTracking.pfnLocateHandJointsEXT(mHandTracking.trackers[hand], &globalInfo, &joint_locations[hand]);
2021 if (!xr_result(mInstance, result, "failed to locate hand %d joints!", hand))
2022 break;
2023
2024 if (joint_locations[hand].isActive)
2025 {
2026 if ((joint_locations[hand].jointLocations[XR_HAND_JOINT_WRIST_EXT].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 &&
2027 (joint_locations[hand].jointLocations[XR_HAND_JOINT_WRIST_EXT].locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0)
2028 {
2029 mControllerType[hand] = ControllerType::HANDS_CONTROLLER;
2030 mInput.handVisible[hand] = true;
2031
2032 Vector3 offVec = Vector3(joint_locations[hand].jointLocations[XR_HAND_JOINT_WRIST_EXT].pose.position);
2033 Quaternion offQuat = Quaternion(joint_locations[hand].jointLocations[XR_HAND_JOINT_WRIST_EXT].pose.orientation);
2034 getControllerOffset(mLastControllerType, hand, true, offVec, offQuat);
2035
2036 mInput.handQuat[hand] = offQuat;
2037 mInput.handPos[hand] = offVec;
2038
2039 if ((joint_velocities[hand].jointVelocities[XR_HAND_JOINT_WRIST_EXT].velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) != 0 &&
2040 (joint_velocities[hand].jointVelocities[XR_HAND_JOINT_WRIST_EXT].velocityFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) != 0)
2041 {
2042 mInput.handVelocity[hand] = joint_velocities[hand].jointVelocities[XR_HAND_JOINT_WRIST_EXT].linearVelocity;
2043 mInput.handAngularVelocity[hand] = joint_velocities[hand].jointVelocities[XR_HAND_JOINT_WRIST_EXT].angularVelocity;
2044 }
2045 else
2046 {
2047 mInput.handVelocity[hand] = Vector3{ 0.0f, 0.0f, 0.0f };
2048 mInput.handAngularVelocity[hand] = Vector3{ 0.0f, 0.0f, 0.0f };
2049 }
2050
2051 int thp = ((joint_locations[hand].jointLocations[XR_HAND_JOINT_THUMB_METACARPAL_EXT].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) ? XR_HAND_JOINT_THUMB_METACARPAL_EXT : XR_HAND_JOINT_THUMB_PROXIMAL_EXT;
2052 int inp = ((joint_locations[hand].jointLocations[XR_HAND_JOINT_INDEX_METACARPAL_EXT].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && !mIsPico) ? XR_HAND_JOINT_INDEX_METACARPAL_EXT : XR_HAND_JOINT_INDEX_PROXIMAL_EXT;
2053 int mdp = ((joint_locations[hand].jointLocations[XR_HAND_JOINT_MIDDLE_METACARPAL_EXT].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && !mIsPico) ? XR_HAND_JOINT_MIDDLE_METACARPAL_EXT : XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT;
2054 int rgp = ((joint_locations[hand].jointLocations[XR_HAND_JOINT_RING_METACARPAL_EXT].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && !mIsPico) ? XR_HAND_JOINT_RING_METACARPAL_EXT : XR_HAND_JOINT_RING_PROXIMAL_EXT;
2055 int ltp = ((joint_locations[hand].jointLocations[XR_HAND_JOINT_LITTLE_METACARPAL_EXT].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) ? XR_HAND_JOINT_LITTLE_METACARPAL_EXT : XR_HAND_JOINT_LITTLE_PROXIMAL_EXT;
2056
2057 for (uint32_t j = 0; j < joint_locations[hand].jointCount; j++)
2058 {
2059 if ((mIsPico && (j == XR_HAND_JOINT_INDEX_METACARPAL_EXT || j == XR_HAND_JOINT_MIDDLE_METACARPAL_EXT || j == XR_HAND_JOINT_RING_METACARPAL_EXT)) ||
2060 ((joint_locations[hand].jointLocations[j].locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) == 0))
2061 {
2062 mHandTracking.bones[hand][j].valid = false;
2063 }
2064 else
2065 {
2066 if ((mLastControllerType == MS_MOTION_CONTROLLER) && j == XR_HAND_JOINT_WRIST_EXT && (hand == VrController::Left))
2067 mHandTracking.bones[hand][j].rotation = Quaternion(joint_locations[hand].jointLocations[j].pose.orientation) * Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
2068 else
2069 mHandTracking.bones[hand][j].rotation = Quaternion(joint_locations[hand].jointLocations[j].pose.orientation);
2070
2071 Quaternion qref(1.0, 0.0, 0.0, 0.0);
2072
2073 if (j == thp ||
2074 j == inp ||
2075 j == mdp ||
2076 j == rgp ||
2077 j == ltp ||
2078 j == 0)
2079 qref = Quaternion(joint_locations[hand].jointLocations[XR_HAND_JOINT_WRIST_EXT].pose.orientation);
2080 else
2081 qref = Quaternion(joint_locations[hand].jointLocations[j-1].pose.orientation);
2082
2083 if (j > 1)
2084 mHandTracking.bones[hand][j].rotation = mHandTracking.bones[hand][j].rotation.Inverse() * qref;
2085
2086 mHandTracking.bones[hand][j].position = joint_locations[hand].jointLocations[j].pose.position;
2087 mHandTracking.bones[hand][j].valid = true;
2088 }
2089 }
2090
2091 bool mpinch = false;
2092 bool ipinch = false;
2093
2094 // hand gesture based on distances
2095 Vector3 palmpos = mHandTracking.bones[hand][XR_HAND_JOINT_PALM_EXT].position;
2096 float thmubpdist = Vector3(mHandTracking.bones[hand][XR_HAND_JOINT_INDEX_TIP_EXT].position).distance(mHandTracking.bones[hand][XR_HAND_JOINT_THUMB_TIP_EXT].position);
2097 float thmubidist = Vector3(mHandTracking.bones[hand][XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT].position).distance(mHandTracking.bones[hand][XR_HAND_JOINT_THUMB_TIP_EXT].position);
2098 float indexdist = palmpos.distance(mHandTracking.bones[hand][XR_HAND_JOINT_INDEX_TIP_EXT].position);
2099 float middist = palmpos.distance(mHandTracking.bones[hand][XR_HAND_JOINT_MIDDLE_TIP_EXT].position);
2100 float ringdist = palmpos.distance(mHandTracking.bones[hand][XR_HAND_JOINT_RING_TIP_EXT].position);
2101 float littledist = palmpos.distance(mHandTracking.bones[hand][XR_HAND_JOINT_LITTLE_TIP_EXT].position);
2102
2103 if (mHandTracking.gestures)
2104 {
2105 mInput.buttonStates[hand][ControllerButtons::btnMenu] = ((aim_states[hand].status & XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB) != 0) ? true : false;
2106 mpinch = ((aim_states[hand].status & XR_HAND_TRACKING_AIM_MIDDLE_PINCHING_BIT_FB) != 0) ? true : false;
2107 ipinch = ((aim_states[hand].status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0) ? true : false;
2108 mInput.analogStates[hand][ControllerAnalog::analogTrigger] = aim_states[hand].pinchStrengthIndex;
2109 mInput.analogStates[hand][ControllerAnalog::analogGrip] = aim_states[hand].pinchStrengthMiddle;
2110 }
2111 else
2112 {
2113 mInput.analogStates[hand][ControllerAnalog::analogTrigger] = (thmubpdist <= 0.035f) ? 1.0f - std::min(1.0f, std::max(0.0f, (1.0f / 3.5f) * ((thmubpdist - 0.01f) * 100.0f))) : 0.0f;
2114 ipinch = (mInput.analogStates[hand][ControllerAnalog::analogTrigger] >= 0.95) ? true : false;
2115 mpinch = ((middist <= 0.05f) && (ringdist <= 0.05f)) ? true : false;
2116 mInput.analogStates[hand][ControllerAnalog::analogGrip] = mpinch ? 1.0f : 0.0f;
2117 }
2118
2119 if (!mInput.touchStates[hand][ControllerTouch::touchIndexPointing])
2120 mInput.touchStates[hand][ControllerTouch::touchIndexPointing] = ((middist <= 0.05f) && (ringdist <= 0.055f) && (littledist <= 0.065f) && (indexdist > 0.07f)) ? true : false;
2121
2122 if (!mInput.buttonStates[hand][ControllerButtons::btnGrip])
2123 mInput.buttonStates[hand][ControllerButtons::btnGrip] = mpinch;
2124
2125 if (!mInput.buttonStates[hand][ControllerButtons::btnTrigger])
2126 mInput.buttonStates[hand][ControllerButtons::btnTrigger] = ((indexdist <= 0.05f) || ipinch) ? true : false;
2127
2128 if (hand == 0)
2129 {
2130 if (!mInput.buttonStates[hand][ControllerButtons::btnX])
2131 mInput.buttonStates[hand][ControllerButtons::btnX] = (thmubidist <= 0.030f) ? true : false;
2132 }
2133 else
2134 {
2135 if (!mInput.buttonStates[hand][ControllerButtons::btnA])
2136 mInput.buttonStates[hand][ControllerButtons::btnA] = (thmubidist <= 0.030f) ? true : false;
2137 }
2138 }
2139 }
2140 }
2141
2142 if (mInput.handVisible[hand] == false)
2143 {
2144 XrSpaceVelocity spaceVelocity{XR_TYPE_SPACE_VELOCITY};
2145 XrSpaceLocation spaceLocation{XR_TYPE_SPACE_LOCATION, &spaceVelocity};
2146 result = xrLocateSpace(mInput.handSpace[hand], mPlaySpace, frameState.predictedDisplayTime, &spaceLocation);
2147 if (xr_result(mInstance, result, "Unable to locate %d hand action space in app space", hand))
2148 {
2149 if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0)
2150 {
2151 mInput.handVisible[hand] = true;
2152 mControllerType[hand] = ControllerType::CLASSIC_CONTROLLER;
2153
2154 Vector3 offVec = Vector3(spaceLocation.pose.position);
2155 Quaternion offQuat = Quaternion(spaceLocation.pose.orientation);
2156 getControllerOffset(mLastControllerType, hand, false, offVec, offQuat);
2157
2158 mInput.handQuat[hand] = offQuat;
2159 mInput.handPos[hand] = offVec;
2160 }
2161
2162 if ((spaceVelocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) != 0 && (spaceVelocity.velocityFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) != 0)
2163 {
2164 mInput.handVelocity[hand] = spaceVelocity.linearVelocity;
2165 mInput.handAngularVelocity[hand] = spaceVelocity.angularVelocity;
2166 }
2167 else
2168 {
2169 mInput.handVelocity[hand] = Vector3{0.0f, 0.0f, 0.0f};
2170 mInput.handAngularVelocity[hand] = Vector3{0.0f, 0.0f, 0.0f};
2171 }
2172 }
2173 }
2174 }
2175}
2176
2177bool sOpenXr::Update()
2178{
2179 if (!mConnected || mPaused)
2180 return false;
2181
2182 //poll events
2183 processEvents();
2184
2185 if (mRequestRestart)
2186 {
2187 Disconnect();
2188 Connect();
2189 }
2190
2191 for (unsigned int i = 0; i < MAX_VR_CONTROLLERS; i++)
2192 mInput.handVisible[i] = false;
2193
2194 if (mConnected && mSessionRunning)
2195 {
2196 XrResult result;
2197
2198 //poll actions
2199 processActions();
2200
2201 XrFrameState frameState = { XR_TYPE_FRAME_STATE };
2202 XrFrameWaitInfo frameWaitInfo = { XR_TYPE_FRAME_WAIT_INFO };
2203 result = xrWaitFrame(mSession, &frameWaitInfo, &frameState);
2204 if (!xr_result(mInstance, result, "xrWaitFrame() was not successful, exiting..."))
2205 return false;
2206
2207 updateControllers(frameState);
2208
2209 uint32_t view_count = 0;
2210 XrViewLocateInfo view_locate_info = { XR_TYPE_VIEW_LOCATE_INFO, XR_NULL_HANDLE, mViewType, frameState.predictedDisplayTime, mPlaySpace };
2211 XrSpaceLocation view_in_stage = { XR_TYPE_SPACE_LOCATION, XR_NULL_HANDLE, 0, identity_pose };
2212 xrLocateSpace(mViewSpace, mPlaySpace, frameState.predictedDisplayTime, &view_in_stage);
2213
2214 XrViewState view_state = { XR_TYPE_VIEW_STATE, XR_NULL_HANDLE };
2215 result = xrLocateViews(mSession, &view_locate_info, &view_state, (uint32_t)mViews.size(), &view_count, mViews.data());
2216 if (!xr_result(mInstance, result, "Could not locate views"))
2217 return false;
2218
2219 for (uint32_t i = 0; i < view_count; i++)
2220 {
2221 mProjectionViews[i].pose = mViews[i].pose;
2222 mProjectionViews[i].fov = mViews[i].fov;
2223 }
2224
2225 if (view_state.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT)
2226 mPoseValid = true;
2227
2228 if ((view_count > 1) && (view_state.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT))
2229 mLastIPD = Vector3(mViews[0].pose.position).distance(Vector3(mViews[1].pose.position));
2230
2231 if (view_state.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT)
2232 {
2233 mPreviousHmdQuat.w = view_in_stage.pose.orientation.w;
2234 mPreviousHmdQuat.x = view_in_stage.pose.orientation.x;
2235 mPreviousHmdQuat.y = view_in_stage.pose.orientation.y;
2236 mPreviousHmdQuat.z = view_in_stage.pose.orientation.z;
2237 }
2238
2239 if (view_state.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT)
2240 {
2241 mPreviousHmdPos.x = view_in_stage.pose.position.x;
2242 mPreviousHmdPos.y = view_in_stage.pose.position.y;
2243 mPreviousHmdPos.z = view_in_stage.pose.position.z;
2244 }
2245
2246 XrFrameBeginInfo frame_begin_info = { XR_TYPE_FRAME_BEGIN_INFO };
2247
2248 result = xrBeginFrame(mSession, &frame_begin_info);
2249 if (!xr_result(mInstance, result, "failed to begin frame!"))
2250 {
2251 mSkipNextFrame = true;
2252 return false;
2253 }
2254 mSkipNextFrame = false;
2255
2256 mPredictedDisplayTime = frameState.predictedDisplayTime;
2257 }
2258
2259 return true;
2260}
2261
2262void sOpenXr::UpdateTextures(SCOL_PTR_TYPE leftTexture, SCOL_PTR_TYPE rightTexture)
2263{
2264 if (mConnected && mSessionRunning && !mPaused && !mSkipNextFrame)
2265 {
2266 XrResult result;
2267 uint32_t view_count = (uint32_t)mViews.size();
2268 bool renderState = true;
2269 bool sessionActive = mState == XR_SESSION_STATE_READY || mState == XR_SESSION_STATE_VISIBLE || mState == XR_SESSION_STATE_FOCUSED;
2270 for (uint32_t i = 0; i < view_count; i++)
2271 {
2272 SCOL_PTR_TYPE eyetex = ((i == 0) ? leftTexture : rightTexture);
2273 if (!eyetex || !sessionActive)
2274 {
2275 renderState = false;
2276 break;
2277 }
2278
2279 XrSwapchainImageAcquireInfo acquire_info = { XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
2280 uint32_t acquired_index = 0;
2281 result = xrAcquireSwapchainImage(mSwapchains[i], &acquire_info, &acquired_index);
2282 if (!xr_result(mInstance, result, "failed to acquire swapchain image!"))
2283 {
2284 renderState = false;
2285 //break;
2286 }
2287
2288 XrSwapchainImageWaitInfo wait_info = { XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, XR_NULL_HANDLE, XR_INFINITE_DURATION };
2289 result = xrWaitSwapchainImage(mSwapchains[i], &wait_info);
2290 if (!xr_result(mInstance, result, "failed to wait for swapchain image!"))
2291 {
2292 renderState = false;
2293 XrSwapchainImageReleaseInfo release_info = { XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
2294 result = xrReleaseSwapchainImage(mSwapchains[i], &release_info);
2295 xr_result(mInstance, result, "failed to release swapchain image!");
2296 break;
2297 }
2298
2299 int32_t width = mProjectionViews[i].subImage.imageRect.extent.width;
2300 int32_t height = mProjectionViews[i].subImage.imageRect.extent.height;
2301
2302 mRenderer->UpdateTextures(leftTexture, rightTexture, acquired_index, width, height, i);
2303
2304 XrSwapchainImageReleaseInfo release_info = { XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO };
2305 result = xrReleaseSwapchainImage(mSwapchains[i], &release_info);
2306 if (!xr_result(mInstance, result, "failed to release swapchain image!"))
2307 {
2308 renderState = false;
2309 break;
2310 }
2311 }
2312
2313 // projectionLayers struct reused for every frame
2314 std::vector<XrCompositionLayerBaseHeader*> layers;
2315 XrCompositionLayerFlags flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT|XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT;
2316 if (((mPassthroughFB.supported && mPassthroughFB.available) || mPassthroughHTC.supported) && mPassthroughEnable)
2317 flags |= XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT/*|XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT*/;
2318
2319 XrCompositionLayerProjection layer_proj = { XR_TYPE_COMPOSITION_LAYER_PROJECTION, XR_NULL_HANDLE, flags,
2320 mPlaySpace, view_count, mProjectionViews.data() };
2321
2322 if (sessionActive && renderState)
2323 {
2324 if (mPassthroughFB.supported && mPassthroughEnable && (mPassthroughFB.passthroughLayer != XR_NULL_HANDLE) && mPassthroughFB.available)
2325 {
2326 XrCompositionLayerPassthroughFB passthroughCompLayer = {XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB, XR_NULL_HANDLE,
2327 XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, XR_NULL_HANDLE,
2328 mPassthroughFB.passthroughLayer};
2329
2330 layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader*>(&passthroughCompLayer));
2331 }
2332 else if (mPassthroughHTC.supported && mPassthroughEnable)
2333 {
2334 XrPassthroughColorHTC passthroughColor = { XR_TYPE_PASSTHROUGH_COLOR_HTC, XR_NULL_HANDLE, 1.0f };
2335 XrCompositionLayerPassthroughHTC passthroughCompLayer = { XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC, XR_NULL_HANDLE,
2336 XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, mViewSpace,
2337 mPassthroughHTC.passthroughFeature, passthroughColor};
2338
2339 layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader*>(&passthroughCompLayer));
2340 }
2341
2342 layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer_proj));
2343 }
2344
2345 XrFrameEndInfo frameEndInfo = {};
2346 frameEndInfo.type = XR_TYPE_FRAME_END_INFO;
2347 frameEndInfo.displayTime = mPredictedDisplayTime;
2348 frameEndInfo.layerCount = (uint32_t)layers.size();
2349 frameEndInfo.layers = layers.data();
2350 frameEndInfo.environmentBlendMode = mBlendMode;
2351 frameEndInfo.next = XR_NULL_HANDLE;
2352 result = xrEndFrame(mSession, &frameEndInfo);
2353 if (!xr_result(mInstance, result, "failed to end frame!") && mPassthroughFB.supported && mPassthroughEnable)
2354 {
2355 layers.clear();
2356 layers.push_back(reinterpret_cast<XrCompositionLayerBaseHeader*>(&layer_proj));
2357 frameEndInfo.layerCount = (uint32_t)layers.size();
2358 frameEndInfo.layers = layers.data();
2359 result = xrEndFrame(mSession, &frameEndInfo);
2360 xr_result(mInstance, result, "failed to end frame even with simple layer!");
2361 }
2362 }
2363}
2364
2365bool sOpenXr::GetControllerVisibility(int id)
2366{
2367 return mInput.handVisible[id];
2368}
2369
2370Vector3 sOpenXr::GetControllerPosition(int id)
2371{
2372 return mInput.handPos[id];
2373}
2374
2375Quaternion sOpenXr::GetControllerOrientation(int id)
2376{
2377 return mInput.handQuat[id];
2378}
2379
2380Vector3 sOpenXr::GetControllerVelocity(int id)
2381{
2382 return mInput.handVelocity[id];
2383}
2384
2385Vector3 sOpenXr::GetControllerAngularVelocity(int id)
2386{
2387 return mInput.handAngularVelocity[id];
2388}
2389
2390std::vector<bool> sOpenXr::GetControllerButtonsState(int id)
2391{
2392 if (!mConnected || id >= mInput.buttonStates.size())
2393 {
2394 std::vector<bool> ret;
2395 return ret;
2396 }
2397 return mInput.buttonStates[id];
2398}
2399
2400std::vector<bool> sOpenXr::GetControllerTouchesState(int id)
2401{
2402 if (!mConnected || id >= mInput.touchStates.size())
2403 {
2404 std::vector<bool> ret;
2405 return ret;
2406 }
2407 return mInput.touchStates[id];
2408}
2409
2410std::vector<float> sOpenXr::GetControllerAnalogState(int id)
2411{
2412 if (!mConnected || id >= mInput.analogStates.size())
2413 {
2414 std::vector<float> ret;
2415 return ret;
2416 }
2417 return mInput.analogStates[id];
2418}
2419
2420void sOpenXr::SetControllerVibration(int id, float value)
2421{
2422 if (mState != XR_SESSION_STATE_FOCUSED)
2423 return;
2424
2425 XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
2426 vibration.amplitude = value;
2427 vibration.duration = XR_MIN_HAPTIC_DURATION;
2428 vibration.frequency = XR_FREQUENCY_UNSPECIFIED;
2429
2430 XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
2431 hapticActionInfo.action = mInput.vibrateAction;
2432 hapticActionInfo.subactionPath = mInput.handSubactionPath[id];
2433 xrApplyHapticFeedback(mSession, &hapticActionInfo, (XrHapticBaseHeader*)&vibration);
2434}
2435
2436std::array<Position, MAX_HAND_JOINTS> sOpenXr::GetControllerBones(int id, bool &ret)
2437{
2438 if (!mConnected || !mHandTracking.supported || (id > 1))
2439 ret = false;
2440 else
2441 ret = true;
2442 return mHandTracking.bones[id];
2443}
2444
2445int sOpenXr::GetControllerType(int id)
2446{
2447 if (!mConnected)
2448 return 0;
2449
2450 return mControllerType[id];
2451}
2452
2453bool sOpenXr::IsPassthroughSupported()
2454{
2455 return mPassthroughFB.supported || mPassthroughHTC.supported;
2456}
2457
2458bool sOpenXr::updatePassthrough()
2459{
2460 if (!IsPassthroughSupported())
2461 return false;
2462
2463 XrResult result;
2464 if (mPassthroughEnable)
2465 {
2466 if (mPassthroughFB.supported)
2467 {
2468 result = mPassthroughFB.pfnXrPassthroughStartFBX(mPassthroughFB.passthroughFeature);
2469 if (!xr_result(mInstance, result, "Could not start passthrough!"))
2470 return false;
2471
2472 result = mPassthroughFB.pfnXrPassthroughLayerResumeFBX(mPassthroughFB.passthroughLayer);
2473 if (!xr_result(mInstance, result, "Could not resume passthrough layer!"))
2474 return false;
2475
2476 if (mPassthroughFB.pfnXrPassthroughLayerSetStyleFB)
2477 {
2478 XrPassthroughStyleFB style{ XR_TYPE_PASSTHROUGH_STYLE_FB };
2479 style.textureOpacityFactor = 1.0f;
2480 style.edgeColor = { 0.0f, 0.0f, 0.0f, 0.0f };
2481 mPassthroughFB.pfnXrPassthroughLayerSetStyleFB(mPassthroughFB.passthroughLayer, &style);
2482 }
2483
2484 mPassthroughFB.available = true;
2485 }
2486 else if (mPassthroughHTC.supported)
2487 {
2488 XrPassthroughCreateInfoHTC passthroughInfo = { XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, XR_NULL_HANDLE, XR_PASSTHROUGH_FORM_PLANAR_HTC };
2489 result = mPassthroughHTC.pfnXrCreatePassthroughHTC(mSession, &passthroughInfo, &mPassthroughHTC.passthroughFeature);
2490 if (!xr_result(mInstance, result, "Could not start passthrough!"))
2491 return false;
2492 }
2493 SetSceneBlendMode(XR_ENVIRONMENT_BLEND_MODE_ALPHA_BLEND);
2494 }
2495 else
2496 {
2497 if (mPassthroughFB.supported)
2498 {
2499 result = mPassthroughFB.pfnXrPassthroughLayerPauseFBX(mPassthroughFB.passthroughLayer);
2500 if (!xr_result(mInstance, result, "Could not pause passthrough layer!"))
2501 return false;
2502
2503 result = mPassthroughFB.pfnXrPassthroughPauseFBX(mPassthroughFB.passthroughFeature);
2504 if (!xr_result(mInstance, result, "Could not pause passthrough!"))
2505 return false;
2506 }
2507 else if (mPassthroughHTC.supported)
2508 {
2509 result = mPassthroughHTC.pfnXrDestroyPassthroughHTC(mPassthroughHTC.passthroughFeature);
2510 mPassthroughHTC.passthroughFeature = XR_NULL_HANDLE;
2511 if (!xr_result(mInstance, result, "Could not stop passthrough!"))
2512 return false;
2513 }
2514
2515 SetSceneBlendMode(XR_ENVIRONMENT_BLEND_MODE_OPAQUE);
2516 }
2517 return true;
2518}
2519
2520bool sOpenXr::SetPassthroughState(bool state)
2521{
2522 if (!IsPassthroughSupported() || (mPassthroughEnable == state))
2523 return false;
2524
2525 mPassthroughEnable = state;
2526
2527 updatePassthrough();
2528 return true;
2529}
2530
2531bool sOpenXr::createPassthrough()
2532{
2533 if (!mPassthroughFB.supported)
2534 return false;
2535
2536 XrResult result;
2537 /*
2538 XrSystemPassthroughProperties2FB passthrough_props{XR_TYPE_SYSTEM_PASSTHROUGH_PROPERTIES2_FB};
2539 XrSystemProperties system_props = { XR_TYPE_SYSTEM_PROPERTIES, &passthrough_props};
2540
2541 result = xrGetSystemProperties(mInstance, mSystemId, &system_props);
2542 if (!xr_result(mInstance, result, "Failed to get System properties"))
2543 return false;
2544
2545 XrPassthroughFlagsFB flags = (passthrough_props.capabilities & XR_PASSTHROUGH_CAPABILITY_LAYER_DEPTH_BIT_FB) ? XR_PASSTHROUGH_LAYER_DEPTH_BIT_FB : 0;
2546 */
2547 XrPassthroughCreateInfoFB passthroughCreateInfo = {XR_TYPE_PASSTHROUGH_CREATE_INFO_FB, XR_NULL_HANDLE, 0};
2548 result = mPassthroughFB.pfnXrCreatePassthroughFBX(mSession, &passthroughCreateInfo, &mPassthroughFB.passthroughFeature);
2549 if (!xr_result(mInstance, result,"Failed to create a passthrough feature."))
2550 return mPassthroughFB.supported = false;
2551
2552 // Create passthrough layer
2553 XrPassthroughLayerCreateInfoFB layerCreateInfo = {XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB, XR_NULL_HANDLE,
2554 mPassthroughFB.passthroughFeature, 0,
2555 XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB};
2556 result = mPassthroughFB.pfnXrCreatePassthroughLayerFBX(mSession, &layerCreateInfo, &mPassthroughFB.passthroughLayer);
2557 if (!xr_result(mInstance, result, "Failed to create and start a passthrough layer"))
2558 return mPassthroughFB.supported = false;
2559
2560 return true;
2561}
2562
2563void sOpenXr::cleanPassthrough()
2564{
2565 XrResult result;
2566 if (mPassthroughFB.supported)
2567 {
2568 if (mPassthroughEnable)
2569 {
2570 result = mPassthroughFB.pfnXrPassthroughLayerPauseFBX(mPassthroughFB.passthroughLayer);
2571 xr_result(mInstance, result, "Could not pause passthrough layer!");
2572
2573 result = mPassthroughFB.pfnXrPassthroughPauseFBX(mPassthroughFB.passthroughFeature);
2574 xr_result(mInstance, result, "Could not pause passthrough!");
2575 }
2576
2577 // destroy passthrough layer
2578 if (mPassthroughFB.passthroughLayer != XR_NULL_HANDLE)
2579 {
2580 result = mPassthroughFB.pfnXrDestroyPassthroughLayerFBX(mPassthroughFB.passthroughLayer);
2581 xr_result(mInstance, result, "Failed to destroy a passthrough layer");
2582 mPassthroughFB.passthroughLayer = XR_NULL_HANDLE;
2583 }
2584
2585 // destroy the feature
2586 if (mPassthroughFB.passthroughFeature != XR_NULL_HANDLE)
2587 {
2588 result = mPassthroughFB.pfnXrDestroyPassthroughFBX(mPassthroughFB.passthroughFeature);
2589 xr_result(mInstance, result, "Failed to destroy a passthrough feature.");
2590 mPassthroughFB.passthroughFeature = XR_NULL_HANDLE;
2591 }
2592 }
2593
2594 if (mPassthroughHTC.supported && mPassthroughHTC.passthroughFeature != XR_NULL_HANDLE)
2595 {
2596 result = mPassthroughHTC.pfnXrDestroyPassthroughHTC(mPassthroughHTC.passthroughFeature);
2597 mPassthroughHTC.passthroughFeature = XR_NULL_HANDLE;
2598 xr_result(mInstance, result, "Failed to destroy a passthrough feature.");
2599 }
2600}
sOpenXr(RenderSystem rsys=XR_OPENGL_RENDERER, float scaleRatio=1.0f)
Definition sOpenXR.cpp:106