OpenXRVkActionsManager.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Component/TickBus.h>
  9. #include <AzCore/Settings/SettingsRegistry.h>
  10. #include <AzFramework/Asset/AssetSystemBus.h>
  11. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  12. //#include <OpenXRVk/OpenXRInteractionProfileBus.h>
  13. #include <OpenXRVk/OpenXRVkUtils.h>
  14. #include <OpenXRVk/OpenXRVkReferenceSpacesInterface.h>
  15. #include "OpenXRVkActionsManager.h"
  16. namespace OpenXRVk
  17. {
  18. // The action type of an action will always be the action type of the first action path descriptor.
  19. static XrActionType GetActionTypeFromActionDescriptor(const OpenXRInteractionProfilesAsset& interactionProfilesAsset,
  20. const OpenXRActionDescriptor& actionDescriptor)
  21. {
  22. if (actionDescriptor.m_actionPathDescriptors.empty())
  23. {
  24. AZ_Error(ActionsManager::LogName, false, "Need at least one action path descriptor for action with name [%s].", actionDescriptor.m_name.c_str());
  25. return XR_ACTION_TYPE_MAX_ENUM;
  26. }
  27. const auto& actionPathDescriptor = actionDescriptor.m_actionPathDescriptors[0];
  28. auto interactionProfileDescriptor = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName);
  29. if (!interactionProfileDescriptor)
  30. {
  31. AZ_Error(ActionsManager::LogName, false, "Couldn't find interaction profile named [%s].", actionPathDescriptor.m_interactionProfileName.c_str());
  32. return XR_ACTION_TYPE_MAX_ENUM;
  33. }
  34. auto userPathDescriptor = interactionProfileDescriptor->GetUserPathDescriptor(actionPathDescriptor.m_userPathName);
  35. if (!userPathDescriptor)
  36. {
  37. AZ_Error(ActionsManager::LogName, false, "Couldn't find user path descriptor with name [%s].", actionPathDescriptor.m_userPathName.c_str());
  38. return XR_ACTION_TYPE_MAX_ENUM;
  39. }
  40. // See if the component is owned by the user path.
  41. auto componentPathDescriptor = userPathDescriptor->GetComponentPathDescriptor(actionPathDescriptor.m_componentPathName);
  42. if (!componentPathDescriptor)
  43. {
  44. componentPathDescriptor = interactionProfileDescriptor->GetCommonComponentPathDescriptor(actionPathDescriptor.m_componentPathName);
  45. if (!componentPathDescriptor)
  46. {
  47. AZ_Error(ActionsManager::LogName, false, "Couldn't find component path descriptor for component path with name [%s].", actionPathDescriptor.m_componentPathName.c_str());
  48. return XR_ACTION_TYPE_MAX_ENUM;
  49. }
  50. }
  51. return componentPathDescriptor->GetXrActionType();
  52. }
  53. AZStd::string ActionsManager::GetActionSetsAssetPath()
  54. {
  55. // Asset Cache relative path
  56. static constexpr char DefaultActionsAssetPath[] = "openxrvk/default.xractions";
  57. static constexpr char ActionSetsAssetPathRegistryKey[] = "/O3DE/Atom/OpenXR/ActionSetsAsset";
  58. auto settingsRegistry = AZ::SettingsRegistry::Get();
  59. if (settingsRegistry)
  60. {
  61. AZStd::string customPath;
  62. bool keyExists = settingsRegistry->Get(customPath, ActionSetsAssetPathRegistryKey);
  63. if (keyExists && !customPath.empty())
  64. {
  65. AZ_Printf(LogName, "The application defined a custom ActionSets asset at path [%s].\n",
  66. customPath.c_str());
  67. return customPath;
  68. }
  69. }
  70. AZ_Printf(LogName, "The application will use the default ActionSets asset at path [%s].\n",
  71. DefaultActionsAssetPath);
  72. return AZStd::string(DefaultActionsAssetPath);
  73. }
  74. bool ActionsManager::LoadActionSetAssetAsync()
  75. {
  76. auto productPath = GetActionSetsAssetPath();
  77. auto assetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(productPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error);
  78. if (!assetId.IsValid())
  79. {
  80. AZ_Error(LogName, false, "No ActionSet Asset named [%s] was found in the asset catalog.", productPath.c_str());
  81. return true; // We return true, just in case we are running on the Editor and the user needs the Editor to verify the content of the asset.
  82. }
  83. AZ::Data::AssetBus::Handler::BusConnect(assetId);
  84. m_actionSetAsset = AZ::Data::AssetManager::Instance().GetAsset(assetId,
  85. azrtti_typeid<OpenXRActionSetsAsset>(), AZ::Data::AssetLoadBehavior::PreLoad);
  86. m_actionSetAsset.QueueLoad();
  87. return true;
  88. }
  89. ActionsManager::~ActionsManager()
  90. {
  91. if (AZ::Data::AssetBus::Handler::BusIsConnected())
  92. {
  93. AZ::Data::AssetBus::Handler::BusDisconnect();
  94. }
  95. }
  96. bool ActionsManager::Init(XrInstance xrInstance, XrSession xrSession)
  97. {
  98. m_xrInstance = xrInstance;
  99. m_xrSession = xrSession;
  100. auto outcome = SetBaseReferenceSpaceForPoseActions(IOpenXRReferenceSpaces::ReferenceSpaceNameView);
  101. if (!outcome.IsSuccess())
  102. {
  103. const auto outcomeMsg = outcome.TakeError();
  104. auto errorMsg = AZStd::string::format("Failed to set [%s] as the default base visualized space. Reason:\n%s.",
  105. IOpenXRReferenceSpaces::ReferenceSpaceNameView, outcomeMsg.c_str());
  106. AZ_Assert(false, "%s", errorMsg.c_str());
  107. AZ_Error(LogName, false, "%s", errorMsg.c_str());
  108. return false;
  109. }
  110. return LoadActionSetAssetAsync();
  111. }
  112. void ActionsManager::InitInternal()
  113. {
  114. AZ_Assert(m_actionSetAsset.IsReady(), "Action Sets asset should be ready!");
  115. AZ_Assert(m_actionSetAsset->m_interactionProfilesAsset.IsReady(), "Was expecting that the Asset System would have loaded the InteractionProfiles Asset.");
  116. const auto interactionProfilesAsset = m_actionSetAsset->m_interactionProfilesAsset;
  117. SuggestedBindingsPerProfile suggestedBindingsPerProfile;
  118. for (const auto& actionSetDescriptor : m_actionSetAsset->m_actionSetDescriptors)
  119. {
  120. if (!InitActionSet(*interactionProfilesAsset, actionSetDescriptor, suggestedBindingsPerProfile))
  121. {
  122. return;
  123. }
  124. }
  125. if (suggestedBindingsPerProfile.empty())
  126. {
  127. AZ_Printf(LogName, "This application will run without actions.\n");
  128. return;
  129. }
  130. // Register the bindings for each active interaction profile.
  131. uint32_t activeProfileCount = 0;
  132. for (const auto& [profileName, suggestedBindings] : suggestedBindingsPerProfile)
  133. {
  134. XrInteractionProfileSuggestedBinding xrSuggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
  135. xrSuggestedBindings.interactionProfile = suggestedBindings.m_profileXrPath;
  136. xrSuggestedBindings.suggestedBindings = suggestedBindings.m_suggestedBindingsList.data();
  137. xrSuggestedBindings.countSuggestedBindings = static_cast<uint32_t>(suggestedBindings.m_suggestedBindingsList.size());
  138. XrResult result = xrSuggestInteractionProfileBindings(m_xrInstance, &xrSuggestedBindings);
  139. if (IsError(result))
  140. {
  141. PrintXrError(LogName, result, "Got an error during suggested bindings registration for profile [%s].", profileName.c_str());
  142. }
  143. else
  144. {
  145. AZ_Printf(LogName, "Successfully registred action bindings for profile [%s].\n", profileName.c_str());
  146. activeProfileCount++;
  147. }
  148. }
  149. if (activeProfileCount < 1)
  150. {
  151. m_topLevelUserPaths.clear();
  152. AZ_Error(LogName, false, "Failed to activate at least one interaction profile. This application will run without actions.\n");
  153. return;
  154. }
  155. AZStd::vector<XrActionSet> xrActionSets;
  156. xrActionSets.reserve(m_actionSets.size());
  157. size_t actionSetIdx = 0;
  158. for (const auto& actionSetInfo : m_actionSets)
  159. {
  160. xrActionSets.push_back(actionSetInfo.m_xrActionSet);
  161. m_activeActionSets.set(actionSetIdx, true); // By default all actionSets will be active.
  162. actionSetIdx++;
  163. }
  164. RecreateXrActiveActionSets();
  165. XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
  166. attachInfo.countActionSets = static_cast<uint32_t>(xrActionSets.size());
  167. attachInfo.actionSets = xrActionSets.data();
  168. XrResult result = xrAttachSessionActionSets(m_xrSession, &attachInfo);
  169. if (IsError(result))
  170. {
  171. m_xrActiveActionSets.clear();
  172. PrintXrError(LogName, result, "Failed to attach %zu action sets to the session.", xrActionSets.size());
  173. }
  174. AZ_Printf(LogName, "Successfully configure the Action Sets.");
  175. }
  176. bool ActionsManager::SyncActions(XrTime predictedDisplayTime)
  177. {
  178. if (m_xrActiveActionSets.empty())
  179. {
  180. // Nothing to do
  181. return true;
  182. }
  183. m_predictedDisplaytime = predictedDisplayTime;
  184. XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO };
  185. syncInfo.countActiveActionSets = aznumeric_cast<uint32_t>(m_xrActiveActionSets.size());
  186. syncInfo.activeActionSets = m_xrActiveActionSets.data();
  187. XrResult result = xrSyncActions(m_xrSession, &syncInfo);
  188. if (IsError(result))
  189. {
  190. PrintXrError(LogName, result, "Failed to sync %zu actionSets.\n", m_xrActiveActionSets.size());
  191. return false;
  192. }
  193. return true;
  194. }
  195. bool ActionsManager::InitActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset,
  196. const OpenXRActionSetDescriptor& actionSetDescriptor,
  197. SuggestedBindingsPerProfile& suggestedBindingsPerProfile)
  198. {
  199. // Create an action set.
  200. XrActionSetCreateInfo actionSetCreateInfo{};
  201. actionSetCreateInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO;
  202. azstrcpy(actionSetCreateInfo.actionSetName, sizeof(actionSetCreateInfo.actionSetName), actionSetDescriptor.m_name.c_str());
  203. const char* localizedNameCStr = actionSetDescriptor.m_name.c_str();
  204. if (!actionSetDescriptor.m_localizedName.empty())
  205. {
  206. localizedNameCStr = actionSetDescriptor.m_localizedName.c_str();
  207. }
  208. azstrcpy(actionSetCreateInfo.localizedActionSetName, sizeof(actionSetCreateInfo.localizedActionSetName), localizedNameCStr);
  209. actionSetCreateInfo.priority = actionSetDescriptor.m_priority;
  210. {
  211. ActionSetInfo newActionSetInfo;
  212. newActionSetInfo.m_name = actionSetDescriptor.m_name;
  213. XrResult result = xrCreateActionSet(m_xrInstance, &actionSetCreateInfo, &newActionSetInfo.m_xrActionSet);
  214. if (IsError(result))
  215. {
  216. PrintXrError(LogName, result, "Failed to instantiate actionSet named [%s].", actionSetDescriptor.m_name.c_str());
  217. return false;
  218. }
  219. m_actionSets.emplace_back(AZStd::move(newActionSetInfo));
  220. }
  221. ActionSetInfo& newActionSetInfo = m_actionSets.back();
  222. for (const auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors)
  223. {
  224. if (!AddActionToActionSet(interactionProfilesAsset, newActionSetInfo, actionDescriptor, suggestedBindingsPerProfile))
  225. {
  226. AZ_Error(LogName, false, "Failed to initialize action bindings for action named [%s] under actionSet named [%s].",
  227. actionDescriptor.m_name.c_str(), actionSetDescriptor.m_name.c_str());
  228. return false;
  229. }
  230. }
  231. return true;
  232. }
  233. bool ActionsManager::AddActionToActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset,
  234. ActionSetInfo& actionSetInfo,
  235. const OpenXRActionDescriptor& actionDescriptor,
  236. SuggestedBindingsPerProfile& suggestedBindingsPerProfile)
  237. {
  238. // One OpenXRAction object will become one XrAction.
  239. // An OpenXRAction contains a list of OpenXRActionPath that need to be bound.
  240. // The action type for each XrAction will be the same and it will be determined by
  241. // the action type of the first action in the list.
  242. AZ_Assert(!actionDescriptor.m_actionPathDescriptors.empty(), "An action descriptor must contain at least one action path descriptor.");
  243. XrActionType actionType = GetActionTypeFromActionDescriptor(interactionProfilesAsset, actionDescriptor);
  244. XrSpace newXrActionSpace = XR_NULL_HANDLE; // Optional.
  245. XrAction newXrAction = CreateXrActionAndXrSpace(actionSetInfo, actionDescriptor, actionType, newXrActionSpace);
  246. if (newXrAction == XR_NULL_HANDLE)
  247. {
  248. return false;
  249. }
  250. // For each actionPath in the list, create the XrPath and its binding.
  251. const auto additionalBindingsCount = AppendActionBindings(interactionProfilesAsset, actionDescriptor, newXrAction, suggestedBindingsPerProfile);
  252. if (additionalBindingsCount < 1)
  253. {
  254. // This action has no bindings. Don't add it to the active actions list.
  255. AZ_Warning(LogName, false, "The action [%s] had no bindings!.\n", actionDescriptor.m_name.c_str());
  256. if (newXrActionSpace != XR_NULL_HANDLE)
  257. {
  258. xrDestroySpace(newXrActionSpace);
  259. }
  260. xrDestroyAction(newXrAction);
  261. return true;
  262. }
  263. m_actions.push_back({});
  264. auto& newActionInfo = m_actions.back();
  265. newActionInfo.m_name = actionDescriptor.m_name;
  266. newActionInfo.m_actionType = actionType;
  267. newActionInfo.m_xrAction = newXrAction;
  268. newActionInfo.m_xrSpace = newXrActionSpace;
  269. uint16_t newActionIndex = aznumeric_cast<uint16_t>(m_actions.size() - 1);
  270. actionSetInfo.m_actions.emplace(actionDescriptor.m_name, IOpenXRActions::ActionHandle(newActionIndex));
  271. return true;
  272. }
  273. XrAction ActionsManager::CreateXrActionAndXrSpace(const ActionSetInfo& actionSetInfo,
  274. const OpenXRActionDescriptor& actionDescriptor, const XrActionType actionType, XrSpace& newXrActionSpace) const
  275. {
  276. XrActionCreateInfo actionCreateInfo{ XR_TYPE_ACTION_CREATE_INFO };
  277. actionCreateInfo.actionType = actionType;
  278. azstrcpy(actionCreateInfo.actionName, sizeof(actionCreateInfo.actionName), actionDescriptor.m_name.c_str());
  279. const char* localizedNameCStr = actionDescriptor.m_name.c_str();
  280. if (!actionDescriptor.m_localizedName.empty())
  281. {
  282. localizedNameCStr = actionDescriptor.m_localizedName.c_str();
  283. }
  284. azstrcpy(actionCreateInfo.localizedActionName, sizeof(actionCreateInfo.localizedActionName), localizedNameCStr);
  285. actionCreateInfo.countSubactionPaths = 0; // Subactions are not supported.
  286. actionCreateInfo.subactionPaths = nullptr; // Subactions are not supported.
  287. XrAction newXrAction;
  288. XrResult result = xrCreateAction(actionSetInfo.m_xrActionSet, &actionCreateInfo, &newXrAction);
  289. if (IsError(result))
  290. {
  291. PrintXrError(LogName, result, "Failed to create XrAction named %s.\n", actionDescriptor.m_name.c_str());
  292. return XR_NULL_HANDLE;
  293. }
  294. // The space will be relevant if the action type is POSE.
  295. newXrActionSpace = XR_NULL_HANDLE;
  296. if (actionType == XR_ACTION_TYPE_POSE_INPUT)
  297. {
  298. XrActionSpaceCreateInfo actionSpaceInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
  299. actionSpaceInfo.action = newXrAction;
  300. actionSpaceInfo.poseInActionSpace.orientation.w = 1.f; // Make it an identity quaterion.
  301. result = xrCreateActionSpace(m_xrSession, &actionSpaceInfo, &newXrActionSpace);
  302. if (IsError(result))
  303. {
  304. xrDestroyAction(newXrAction);
  305. PrintXrError(LogName, result, "Failed to create XrSpace for action named %s.\n", actionDescriptor.m_name.c_str());
  306. return XR_NULL_HANDLE;
  307. }
  308. }
  309. return newXrAction;
  310. }
  311. uint32_t ActionsManager::AppendActionBindings(const OpenXRInteractionProfilesAsset& interactionProfilesAsset,
  312. const OpenXRActionDescriptor& actionDescriptor, XrAction xrAction,
  313. SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const
  314. {
  315. uint32_t additionalBindingsCount = 0;
  316. for (const auto& actionPathDescriptor : actionDescriptor.m_actionPathDescriptors)
  317. {
  318. auto interactionProfileDescriptor = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName);
  319. if (!interactionProfileDescriptor)
  320. {
  321. AZ_Error(LogName, false, "Couldn't find interaction profile descriptor with name [%s].", actionPathDescriptor.m_interactionProfileName.c_str());
  322. return false;
  323. }
  324. auto userPathDescriptor = interactionProfileDescriptor->GetUserPathDescriptor(actionPathDescriptor.m_userPathName);
  325. if (!userPathDescriptor)
  326. {
  327. AZ_Error(LogName, false, "Couldn't find user path descriptor with name [%s].", actionPathDescriptor.m_userPathName.c_str());
  328. return false;
  329. }
  330. m_topLevelUserPaths.emplace(userPathDescriptor->m_path);
  331. const auto absoluteComponentPath = interactionProfileDescriptor->GetComponentAbsolutePath(*userPathDescriptor, actionPathDescriptor.m_componentPathName);
  332. if (absoluteComponentPath.empty())
  333. {
  334. AZ_Error(LogName, false, "Failed to retrieve the absolute action path for profile [%s], user path [%s], component path [%s].\n",
  335. actionPathDescriptor.m_interactionProfileName.c_str(), actionPathDescriptor.m_userPathName.c_str(), actionPathDescriptor.m_componentPathName.c_str());
  336. return false;
  337. }
  338. XrPath xrBindingPath;
  339. XrResult result = xrStringToPath(m_xrInstance, absoluteComponentPath.c_str(), &xrBindingPath);
  340. if (IsError(result))
  341. {
  342. PrintXrError(LogName, result, "Failed to create XrPath for action with profile [%s], absolute path [%s].\n",
  343. actionPathDescriptor.m_interactionProfileName.c_str(), absoluteComponentPath.c_str());
  344. continue;
  345. }
  346. // If the interaction profile is not in the dictionary, then add it.
  347. if (!suggestedBindingsPerProfile.contains(interactionProfileDescriptor->m_name))
  348. {
  349. XrPath profileXrPath;
  350. result = xrStringToPath(m_xrInstance, interactionProfileDescriptor->m_path.c_str(), &profileXrPath);
  351. if (IsError(result))
  352. {
  353. PrintXrError(LogName, result, "Failed to get profile [%s] XrPath while working on action [%s]",
  354. interactionProfileDescriptor->m_path.c_str(), actionDescriptor.m_name.c_str());
  355. continue;
  356. }
  357. suggestedBindingsPerProfile.emplace(interactionProfileDescriptor->m_name, SuggestedBindings{});
  358. SuggestedBindings& newProfileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_name);
  359. newProfileBindings.m_profileXrPath = profileXrPath;
  360. }
  361. SuggestedBindings& profileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_name);
  362. XrActionSuggestedBinding binding;
  363. binding.action = xrAction;
  364. binding.binding = xrBindingPath;
  365. profileBindings.m_suggestedBindingsList.push_back(binding);
  366. additionalBindingsCount++;
  367. }
  368. return additionalBindingsCount;
  369. }
  370. void ActionsManager::OnInteractionProfileChanged()
  371. {
  372. if (m_topLevelUserPaths.empty())
  373. {
  374. return;
  375. }
  376. uint32_t activeUserPathsCount = 0;
  377. for (const auto& userPathStr : m_topLevelUserPaths)
  378. {
  379. XrPath xrUserPath;
  380. XrResult result = xrStringToPath(m_xrInstance, userPathStr.c_str(), &xrUserPath);
  381. if (IsError(result))
  382. {
  383. PrintXrError(LogName, result, "Failed to get xrPath for user top path [%s]", userPathStr.c_str());
  384. continue;
  385. }
  386. XrInteractionProfileState profileStateOut{ XR_TYPE_INTERACTION_PROFILE_STATE };
  387. result = xrGetCurrentInteractionProfile(
  388. m_xrSession, xrUserPath, &profileStateOut);
  389. if (IsError(result))
  390. {
  391. PrintXrError(LogName, result, "Failed to get active profile for user top path [%s]", userPathStr.c_str());
  392. continue;
  393. }
  394. if (profileStateOut.interactionProfile == XR_NULL_PATH)
  395. {
  396. AZ_Printf(LogName, "Got an NULL Interaction Profile for [%s].\n", userPathStr.c_str());
  397. continue;
  398. }
  399. AZStd::string activeProfileName = ConvertXrPathToString(m_xrInstance, profileStateOut.interactionProfile);
  400. if (activeProfileName.empty())
  401. {
  402. AZ_Error(LogName, false, "Failed to convert Interaction Profile XrPath to string for user top path [%s]", userPathStr.c_str());
  403. continue;
  404. }
  405. else
  406. {
  407. activeUserPathsCount++;
  408. AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", userPathStr.c_str(), activeProfileName.c_str());
  409. }
  410. }
  411. if (!activeUserPathsCount)
  412. {
  413. AZ_Printf(LogName, "No interaction profile is active at the moment.\n");
  414. }
  415. }
  416. /////////////////////////////////////////////////
  417. /// OpenXRActionsInterface overrides
  418. AZStd::vector<AZStd::string> ActionsManager::GetAllActionSets() const
  419. {
  420. AZStd::vector<AZStd::string> retList;
  421. retList.reserve(m_actionSets.size());
  422. for (const auto& actionSetInfo : m_actionSets)
  423. {
  424. retList.push_back(actionSetInfo.m_name);
  425. }
  426. return retList;
  427. }
  428. AZ::Outcome<bool, AZStd::string> ActionsManager::GetActionSetState(const AZStd::string& actionSetName) const
  429. {
  430. for (size_t i = 0; i < m_actionSets.size(); i++)
  431. {
  432. if (m_actionSets[i].m_name == actionSetName)
  433. {
  434. return AZ::Success(m_activeActionSets[i]);
  435. }
  436. }
  437. return AZ::Failure(
  438. AZStd::string::format("ActionSet with name [%s] not found.", actionSetName.c_str())
  439. );
  440. }
  441. AZ::Outcome<bool, AZStd::string> ActionsManager::SetActionSetState(const AZStd::string& actionSetName, bool activate)
  442. {
  443. for (size_t i = 0; i < m_actionSets.size(); i++)
  444. {
  445. if (m_actionSets[i].m_name == actionSetName)
  446. {
  447. bool prevState = m_activeActionSets[i];
  448. if (prevState != activate)
  449. {
  450. m_activeActionSets[i] = activate;
  451. RecreateXrActiveActionSets();
  452. }
  453. return AZ::Success(prevState);
  454. }
  455. }
  456. return AZ::Failure(
  457. AZStd::string::format("ActionSet with name [%s] not found.", actionSetName.c_str())
  458. );
  459. }
  460. IOpenXRActions::ActionHandle ActionsManager::GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const
  461. {
  462. for (const auto& actionSetInfo : m_actionSets)
  463. {
  464. if (actionSetInfo.m_name != actionSetName)
  465. {
  466. continue;
  467. }
  468. const auto itor = actionSetInfo.m_actions.find(actionName);
  469. if (itor == actionSetInfo.m_actions.end())
  470. {
  471. AZ_Error(LogName, false, "ActionSet with name [%s] does not have an action named [%s].",
  472. actionSetName.c_str(), actionName.c_str());
  473. return IOpenXRActions::ActionHandle{};
  474. }
  475. return AZ::Success(itor->second);
  476. }
  477. AZ_Error(LogName, false, "ActionSet with name [%s] not found.", actionSetName.c_str());
  478. return IOpenXRActions::ActionHandle{};
  479. }
  480. AZ::Outcome<bool, AZStd::string> ActionsManager::GetActionStateBoolean(ActionHandle actionHandle) const
  481. {
  482. if (!actionHandle.IsValid())
  483. {
  484. return AZ::Failure("Invalid actionHandle!");
  485. }
  486. const auto actionIndex = actionHandle.GetIndex();
  487. XrActionStateBoolean state { XR_TYPE_ACTION_STATE_BOOLEAN };
  488. XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
  489. getInfo.action = m_actions[actionIndex].m_xrAction;
  490. XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state);
  491. if (IsError(result))
  492. {
  493. return AZ::Failure(AZStd::string(GetResultString(result)));
  494. }
  495. if (!state.isActive)
  496. {
  497. return AZ::Failure(
  498. AZStd::string::format("Boolean Action [%s] is NOT active.", m_actions[actionIndex].m_name.c_str())
  499. );
  500. }
  501. return AZ::Success(state.currentState);
  502. }
  503. AZ::Outcome<float, AZStd::string> ActionsManager::GetActionStateFloat(ActionHandle actionHandle) const
  504. {
  505. if (!actionHandle.IsValid())
  506. {
  507. return AZ::Failure("Invalid actionHandle!");
  508. }
  509. const auto actionIndex = actionHandle.GetIndex();
  510. XrActionStateFloat state{ XR_TYPE_ACTION_STATE_FLOAT };
  511. XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
  512. getInfo.action = m_actions[actionIndex].m_xrAction;
  513. XrResult result = xrGetActionStateFloat(m_xrSession, &getInfo, &state);
  514. if (IsError(result))
  515. {
  516. return AZ::Failure(AZStd::string(GetResultString(result)));
  517. }
  518. if (!state.isActive)
  519. {
  520. return AZ::Failure(
  521. AZStd::string::format("Float Action [%s] is NOT active.", m_actions[actionIndex].m_name.c_str())
  522. );
  523. }
  524. return AZ::Success(state.currentState);
  525. }
  526. AZ::Outcome<AZ::Vector2, AZStd::string> ActionsManager::GetActionStateVector2(ActionHandle actionHandle) const
  527. {
  528. if (!actionHandle.IsValid())
  529. {
  530. return AZ::Failure("Invalid actionHandle!");
  531. }
  532. const auto actionIndex = actionHandle.GetIndex();
  533. XrActionStateVector2f state{ XR_TYPE_ACTION_STATE_VECTOR2F };
  534. XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
  535. getInfo.action = m_actions[actionIndex].m_xrAction;
  536. XrResult result = xrGetActionStateVector2f(m_xrSession, &getInfo, &state);
  537. if (IsError(result))
  538. {
  539. return AZ::Failure(AZStd::string(GetResultString(result)));
  540. }
  541. if (!state.isActive)
  542. {
  543. return AZ::Failure(
  544. AZStd::string::format("Vector2 Action[%s] is NOT active.", m_actions[actionIndex].m_name.c_str())
  545. );
  546. }
  547. return AZ::Success(AZ::Vector2(state.currentState.x, state.currentState.y));
  548. }
  549. AZ::Outcome<bool, AZStd::string> ActionsManager::SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName)
  550. {
  551. if (visualizedSpaceName == m_baseReferenceSpaceName)
  552. {
  553. return AZ::Success(true);
  554. }
  555. auto visualizedSpacesIface = OpenXRReferenceSpacesInterface::Get();
  556. if (!visualizedSpacesIface)
  557. {
  558. AZ_Assert(false, "The OpenXRReferenceSpacesInterface doesn't exist!");
  559. return AZ::Failure("The OpenXRReferenceSpacesInterface doesn't exist!");
  560. }
  561. const void* opaqueXrSpace = visualizedSpacesIface->GetReferenceSpaceNativeHandle(visualizedSpaceName);
  562. if (!opaqueXrSpace)
  563. {
  564. return AZ::Failure(
  565. AZStd::string::format("Visualized space with name [%s] doesn't exist. Will keep the current base space named [%s]",
  566. visualizedSpaceName.c_str(), m_baseReferenceSpaceName.c_str())
  567. );
  568. }
  569. m_baseReferenceSpaceName = visualizedSpaceName;
  570. m_xrBaseReferenceSpace = reinterpret_cast<XrSpace>(const_cast<void*>(opaqueXrSpace));
  571. return AZ::Success(true);
  572. }
  573. const AZStd::string& ActionsManager::GetBaseReferenceSpaceForPoseActions() const
  574. {
  575. return m_baseReferenceSpaceName;
  576. }
  577. AZ::Outcome<AZ::Transform, AZStd::string> ActionsManager::GetActionStatePose(ActionHandle actionHandle) const
  578. {
  579. if (!actionHandle.IsValid())
  580. {
  581. return AZ::Failure("Invalid actionHandle!");
  582. }
  583. const auto actionIndex = actionHandle.GetIndex();
  584. // First, we need to make sure the Action is active.
  585. XrActionStatePose state{ XR_TYPE_ACTION_STATE_POSE };
  586. XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
  587. getInfo.action = m_actions[actionIndex].m_xrAction;
  588. XrResult result = xrGetActionStatePose(m_xrSession, &getInfo, &state);
  589. if (IsError(result))
  590. {
  591. return AZ::Failure(AZStd::string(GetResultString(result)));
  592. }
  593. if (!state.isActive)
  594. {
  595. return AZ::Failure(
  596. AZStd::string::format("Pose Action [%s] is NOT active.", m_actions[actionIndex].m_name.c_str())
  597. );
  598. }
  599. XrSpaceLocation spaceLocation {XR_TYPE_SPACE_LOCATION};
  600. result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseReferenceSpace, m_predictedDisplaytime, &spaceLocation);
  601. if (IsError(result))
  602. {
  603. return AZ::Failure(AZStd::string(GetResultString(result)));
  604. }
  605. AZ::Vector3 poseLocation = AZ::Vector3::CreateZero();
  606. if (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)
  607. {
  608. poseLocation = AzPositionFromXrPose(spaceLocation.pose);
  609. }
  610. AZ::Quaternion poseOrientation = AZ::Quaternion::CreateIdentity();
  611. if (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
  612. {
  613. poseOrientation = AzQuaternionFromXrPose(spaceLocation.pose);
  614. }
  615. AZ::Transform retPoseTransform = AZ::Transform::CreateFromQuaternionAndTranslation(poseOrientation, poseLocation);
  616. return AZ::Success(retPoseTransform);
  617. }
  618. AZ::Outcome<PoseWithVelocities, AZStd::string> ActionsManager::GetActionStatePoseWithVelocities(ActionHandle actionHandle) const
  619. {
  620. if (!actionHandle.IsValid())
  621. {
  622. return AZ::Failure("Invalid actionHandle!");
  623. }
  624. const auto actionIndex = actionHandle.GetIndex();
  625. XrSpaceVelocity spaceVelocity{ XR_TYPE_SPACE_VELOCITY };
  626. XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION, &spaceVelocity};
  627. XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseReferenceSpace, m_predictedDisplaytime, &spaceLocation);
  628. if (IsError(result))
  629. {
  630. return AZ::Failure(AZStd::string(GetResultString(result)));
  631. }
  632. AZ::Vector3 poseLocation = AZ::Vector3::CreateZero();
  633. AZ::Vector3 linearVelocity = AZ::Vector3::CreateZero();
  634. if (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT)
  635. {
  636. poseLocation = AzPositionFromXrPose(spaceLocation.pose);
  637. if (spaceVelocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT)
  638. {
  639. linearVelocity = AzVector3FromXrVector3(spaceVelocity.linearVelocity);
  640. }
  641. }
  642. AZ::Quaternion poseOrientation = AZ::Quaternion::CreateIdentity();
  643. AZ::Vector3 angularVelocity = AZ::Vector3::CreateZero();
  644. if (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)
  645. {
  646. poseOrientation = AzQuaternionFromXrPose(spaceLocation.pose);
  647. if (spaceVelocity.velocityFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT)
  648. {
  649. angularVelocity = AzVector3FromXrVector3(spaceVelocity.angularVelocity);
  650. }
  651. }
  652. PoseWithVelocities retPoseWithVelocities {
  653. AZ::Transform::CreateFromQuaternionAndTranslation(poseOrientation, poseLocation),
  654. linearVelocity,
  655. angularVelocity
  656. };
  657. return AZ::Success(retPoseWithVelocities);
  658. }
  659. AZ::Outcome<bool, AZStd::string> ActionsManager::ApplyHapticVibrationAction(ActionHandle actionHandle,
  660. uint64_t durationNanos, float frequencyHz, float amplitude)
  661. {
  662. if (!actionHandle.IsValid())
  663. {
  664. return AZ::Failure("Invalid actionHandle!");
  665. }
  666. const auto actionIndex = actionHandle.GetIndex();
  667. // fire haptics using output action
  668. XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
  669. vibration.amplitude = AZStd::clamp(amplitude, 0.0f, 1.0f);
  670. vibration.duration = durationNanos;
  671. vibration.frequency = frequencyHz;
  672. XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
  673. hapticActionInfo.action = m_actions[actionIndex].m_xrAction;
  674. XrResult result = xrApplyHapticFeedback(m_xrSession, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration);
  675. if (IsError(result))
  676. {
  677. return AZ::Failure(AZStd::string(GetResultString(result)));
  678. }
  679. return AZ::Success(true);
  680. }
  681. AZ::Outcome<bool, AZStd::string> ActionsManager::StopHapticVibrationAction(ActionHandle actionHandle)
  682. {
  683. if (!actionHandle.IsValid())
  684. {
  685. return AZ::Failure("Invalid actionHandle!");
  686. }
  687. const auto actionIndex = actionHandle.GetIndex();
  688. // fire haptics using output action
  689. XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
  690. hapticActionInfo.action = m_actions[actionIndex].m_xrAction;
  691. XrResult result = xrStopHapticFeedback(m_xrSession, &hapticActionInfo);
  692. if (IsError(result))
  693. {
  694. return AZ::Failure(AZStd::string(GetResultString(result)));
  695. }
  696. return AZ::Success(true);
  697. }
  698. /// OpenXRActionsInterface overrides
  699. /////////////////////////////////////////////////
  700. void ActionsManager::RecreateXrActiveActionSets()
  701. {
  702. // Recreate and cache the XrActionActionSet list.
  703. m_xrActiveActionSets.clear();
  704. for (size_t i = 0; i < m_actionSets.size(); i++)
  705. {
  706. if (m_activeActionSets[i])
  707. {
  708. XrActiveActionSet activeActionSet{ m_actionSets[i].m_xrActionSet, XR_NULL_PATH };
  709. m_xrActiveActionSets.push_back(activeActionSet);
  710. }
  711. }
  712. }
  713. /////////////////////////////////////////////////
  714. /// AssetBus overrides
  715. void ActionsManager::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  716. {
  717. AZ::Data::AssetBus::Handler::BusDisconnect();
  718. AZ_Printf(LogName, "Successfully loaded the Action Sets Asset [%s].\n", asset.GetHint().c_str());
  719. // Finish ActionsManager initialization on the main loop.
  720. AZ::TickBus::QueueFunction([this, asset]()
  721. {
  722. m_actionSetAsset = asset;
  723. InitInternal();
  724. });
  725. }
  726. void ActionsManager::OnAssetError(AZ::Data::Asset<AZ::Data::AssetData> asset)
  727. {
  728. AZ::Data::AssetBus::Handler::BusDisconnect();
  729. m_actionSetAsset = asset;
  730. // Report the error and do nothing.
  731. AZ_Assert(false, "Application failed to load The Action Sets asset. This application will keep running without user interaction support.");
  732. AZ_Error(LogName, false, "Application failed to load The Action Sets asset. This application will keep running without user interaction support.");
  733. }
  734. /// AssetBus overrides
  735. /////////////////////////////////////////////////
  736. } // namespace OpenXRVk