OpenXRVkActionSetsAsset.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. //! Breadcrumb... Because this asset serializes a field of type AZ::Data::Asset<...>
  9. //! We need to include this file first to avoid the following compiler error:
  10. //! error C2027: use of undefined type 'AZ::SerializeGenericTypeInfo<ValueType>'
  11. //! error C3861: 'GetClassTypeId': identifier not found
  12. //! The error is triggered when calling ->Field("InteractionProfilesAsset", &OpenXRActionSetsAsset::m_interactionProfilesAsset)
  13. #include <AzCore/Asset/AssetSerializer.h>
  14. #include <OpenXRVk/OpenXRVkActionSetsAsset.h>
  15. #include <OpenXRVk/OpenXRVkAssetsValidator.h>
  16. namespace OpenXRVk
  17. {
  18. namespace EditorInternal
  19. {
  20. // This static asset reference is only relevant when the user is creating an OpenXRActionSetsAsset with the
  21. // Asset Editor. Because this variable is a singleton, creating two of these assets at the same time won't be possible.
  22. // But this is not an issue because most likely all OpenXRActionSetsAsset always use the same OpenXRInteractionProfilesAsset.
  23. static AZ::Data::Asset<OpenXRInteractionProfilesAsset> s_asset;
  24. static constexpr char LogName[] = "EditorInternal::OpenXRInteractionProfilesAsset";
  25. static void BlockingReloadAssetIfNotReady(AZ::Data::Asset<OpenXRInteractionProfilesAsset>& profileAsset)
  26. {
  27. if (!profileAsset.GetId().IsValid())
  28. {
  29. return;
  30. }
  31. if (!profileAsset.IsReady())
  32. {
  33. profileAsset.QueueLoad();
  34. if (profileAsset.IsLoading())
  35. {
  36. profileAsset.BlockUntilLoadComplete();
  37. }
  38. }
  39. }
  40. static void SetCurrentInteractionProfilesAsset(AZ::Data::Asset<OpenXRInteractionProfilesAsset>& newProfilesAsset,
  41. bool loadAsset = true)
  42. {
  43. if (!newProfilesAsset.GetId().IsValid())
  44. {
  45. AZ_Printf(LogName, "The user cleared the global OpenXRInteractionProfilesAsset used for ActionSets Asset Editing.");
  46. s_asset = {};
  47. return;
  48. }
  49. if (loadAsset)
  50. {
  51. BlockingReloadAssetIfNotReady(newProfilesAsset);
  52. }
  53. s_asset = newProfilesAsset;
  54. }
  55. static const AZ::Data::Asset<OpenXRInteractionProfilesAsset>& GetCurrentInteractionProfilesAsset(bool loadAsset = true)
  56. {
  57. if (loadAsset)
  58. {
  59. BlockingReloadAssetIfNotReady(s_asset);
  60. }
  61. return s_asset;
  62. }
  63. }
  64. ///////////////////////////////////////////////////////////
  65. /// OpenXRActionPathDescriptor
  66. void OpenXRActionPathDescriptor::Reflect(AZ::ReflectContext* context)
  67. {
  68. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  69. if (serialize)
  70. {
  71. serialize->Class<OpenXRActionPathDescriptor>()
  72. ->Version(1)
  73. ->Field("InteractionProfile", &OpenXRActionPathDescriptor::m_interactionProfileName)
  74. ->Field("UserPath", &OpenXRActionPathDescriptor::m_userPathName)
  75. ->Field("ComponentPath", &OpenXRActionPathDescriptor::m_componentPathName)
  76. ;
  77. AZ::EditContext* edit = serialize->GetEditContext();
  78. if (edit)
  79. {
  80. edit->Class<OpenXRActionPathDescriptor>("OpenXRActionPathDescriptor", "A specific OpenXR I/O action path.")
  81. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  82. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionPathDescriptor::GetEditorText)
  83. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  84. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_interactionProfileName, "Interaction Profile", "The name of the Interaction Profile.")
  85. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnInteractionProfileSelected)
  86. ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetInteractionProfiles)
  87. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_userPathName, "User Path", "Name of the User Path.")
  88. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnUserPathSelected)
  89. ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetUserPaths)
  90. ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_componentPathName, "I/O Component Path", "The name of I/O Component Path.")
  91. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnComponentPathSelected)
  92. ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetComponentPaths)
  93. ;
  94. }
  95. }
  96. }
  97. AZStd::string OpenXRActionPathDescriptor::GetEditorText() const
  98. {
  99. if (m_interactionProfileName.empty())
  100. {
  101. return "<Unknown Interaction Profile>";
  102. }
  103. if (m_userPathName.empty())
  104. {
  105. return "<Unknown User Path>";
  106. }
  107. if (m_componentPathName.empty())
  108. {
  109. return "<Unknown Component Path>";
  110. }
  111. return AZStd::string::format("%s %s %s", m_interactionProfileName.c_str(), m_userPathName.c_str(), m_componentPathName.c_str());
  112. }
  113. AZ::Crc32 OpenXRActionPathDescriptor::OnInteractionProfileSelected()
  114. {
  115. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  116. }
  117. AZStd::vector<AZStd::string> OpenXRActionPathDescriptor::GetInteractionProfiles() const
  118. {
  119. const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset();
  120. if (!interactionProfilesAsset.IsReady())
  121. {
  122. return {};
  123. }
  124. AZStd::vector<AZStd::string> profileNames;
  125. for (const auto& profileDescriptor : interactionProfilesAsset->m_interactionProfileDescriptors)
  126. {
  127. profileNames.push_back(profileDescriptor.m_name);
  128. }
  129. return profileNames;
  130. }
  131. AZ::Crc32 OpenXRActionPathDescriptor::OnUserPathSelected()
  132. {
  133. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  134. }
  135. AZStd::vector<AZStd::string> OpenXRActionPathDescriptor::GetUserPaths() const
  136. {
  137. const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset();
  138. if (!interactionProfilesAsset.IsReady())
  139. {
  140. return {};
  141. }
  142. AZStd::vector<AZStd::string> retList;
  143. const auto profileDescriptor = interactionProfilesAsset->GetInteractionProfileDescriptor(m_interactionProfileName);
  144. if (!profileDescriptor)
  145. {
  146. return retList;
  147. }
  148. for (const auto& userPathDescriptor : profileDescriptor->m_userPathDescriptors)
  149. {
  150. retList.push_back(userPathDescriptor.m_name);
  151. }
  152. return retList;
  153. }
  154. AZ::Crc32 OpenXRActionPathDescriptor::OnComponentPathSelected()
  155. {
  156. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  157. }
  158. AZStd::vector<AZStd::string> OpenXRActionPathDescriptor::GetComponentPaths() const
  159. {
  160. const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset();
  161. if (!interactionProfilesAsset.IsReady())
  162. {
  163. return {};
  164. }
  165. AZStd::vector<AZStd::string> retList;
  166. const auto profileDescriptor = interactionProfilesAsset->GetInteractionProfileDescriptor(m_interactionProfileName);
  167. if (!profileDescriptor)
  168. {
  169. return retList;
  170. }
  171. const auto* userPathDescriptor = profileDescriptor->GetUserPathDescriptor(m_userPathName);
  172. if (!userPathDescriptor)
  173. {
  174. return retList;
  175. }
  176. for (const auto& componentPath : userPathDescriptor->m_componentPathDescriptors)
  177. {
  178. retList.push_back(componentPath.m_name);
  179. }
  180. for (const auto& componentPath : profileDescriptor->m_commonComponentPathDescriptors)
  181. {
  182. retList.push_back(componentPath.m_name);
  183. }
  184. return retList;
  185. }
  186. /// OpenXRActionPathDescriptor
  187. ///////////////////////////////////////////////////////////
  188. ///////////////////////////////////////////////////////////
  189. /// OpenXRActionDescriptor
  190. void OpenXRActionDescriptor::Reflect(AZ::ReflectContext* context)
  191. {
  192. OpenXRActionPathDescriptor::Reflect(context);
  193. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  194. if (serialize)
  195. {
  196. serialize->Class<OpenXRActionDescriptor>()
  197. ->Version(1)
  198. ->Field("Name", &OpenXRActionDescriptor::m_name)
  199. ->Field("LocalizedName", &OpenXRActionDescriptor::m_localizedName)
  200. ->Field("Comment", &OpenXRActionDescriptor::m_comment)
  201. ->Field("ActionPathDescriptors", &OpenXRActionDescriptor::m_actionPathDescriptors)
  202. ;
  203. AZ::EditContext* edit = serialize->GetEditContext();
  204. if (edit)
  205. {
  206. edit->Class<OpenXRActionDescriptor>("OpenXRActionDescriptor", "An action bound to one or more OpenXR Action Paths.")
  207. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  208. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionDescriptor::GetEditorText)
  209. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  210. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_name, "Name", "Runtime identifier for this action.")
  211. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues"))
  212. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_localizedName, "Localized Name", "User friendly display name.")
  213. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues"))
  214. ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &OpenXRActionDescriptor::m_comment, "Comment", "Free form description of this Action Set.")
  215. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_actionPathDescriptors, "Action Paths", "List of action paths bound to this action")
  216. ;
  217. }
  218. }
  219. }
  220. AZStd::string OpenXRActionDescriptor::GetEditorText() const
  221. {
  222. // In addition to showing the Action name, we'll append the Action Data Type
  223. // of the first ActionPath, so it is easy for the developer to see what will be
  224. // the expected data type at runtime.
  225. AZStd::string actionTypeStr = "Unknown Type";
  226. if (!m_actionPathDescriptors.empty())
  227. {
  228. if (EditorInternal::s_asset)
  229. {
  230. actionTypeStr = EditorInternal::s_asset->GetActionPathTypeStr(
  231. m_actionPathDescriptors[0].m_interactionProfileName,
  232. m_actionPathDescriptors[0].m_userPathName,
  233. m_actionPathDescriptors[0].m_componentPathName
  234. );
  235. }
  236. }
  237. AZStd::string actionDescription = AZStd::string::format("%s [%s]", m_name.empty() ? "<Unknown Action>" : m_name.c_str(), actionTypeStr.c_str());
  238. return actionDescription;
  239. }
  240. /// OpenXRActionDescriptor
  241. ///////////////////////////////////////////////////////////
  242. ///////////////////////////////////////////////////////////
  243. /// OpenXRActionSetDescriptor
  244. void OpenXRActionSetDescriptor::Reflect(AZ::ReflectContext* context)
  245. {
  246. OpenXRActionDescriptor::Reflect(context);
  247. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  248. if (serialize)
  249. {
  250. serialize->Class<OpenXRActionSetDescriptor>()
  251. ->Version(1)
  252. ->Field("Name", &OpenXRActionSetDescriptor::m_name)
  253. ->Field("LocalizedName", &OpenXRActionSetDescriptor::m_localizedName)
  254. ->Field("Priority", &OpenXRActionSetDescriptor::m_priority)
  255. ->Field("Comment", &OpenXRActionSetDescriptor::m_comment)
  256. ->Field("ActionDescriptors", &OpenXRActionSetDescriptor::m_actionDescriptors)
  257. ;
  258. AZ::EditContext* edit = serialize->GetEditContext();
  259. if (edit)
  260. {
  261. edit->Class<OpenXRActionSetDescriptor>("OpenXRActionSetDescriptor", "Contains a list of OpenXR Actions.")
  262. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  263. ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionSetDescriptor::GetEditorText)
  264. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  265. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_name, "Name", "Runtime identifier for this action set.")
  266. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues"))
  267. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_localizedName, "Localized Name", "Action set display name.")
  268. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues"))
  269. ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSetDescriptor::m_priority, "Priority", "The higher this value the higher the priority.")
  270. ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &OpenXRActionSetDescriptor::m_comment, "Comment", "Free form description of this Action Set.")
  271. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_actionDescriptors, "Actions", "List of actions for this action set.")
  272. ;
  273. }
  274. }
  275. }
  276. AZStd::string OpenXRActionSetDescriptor::GetEditorText() const
  277. {
  278. if (!m_name.empty())
  279. {
  280. return m_name;
  281. }
  282. return m_localizedName.empty() ? "<Unknown Action Set>" : m_localizedName;
  283. }
  284. /// OpenXRActionSetDescriptor
  285. ///////////////////////////////////////////////////////////
  286. ///////////////////////////////////////////////////////////
  287. /// OpenXRActionBindingsAsset
  288. void OpenXRActionSetsAsset::Reflect(AZ::ReflectContext* context)
  289. {
  290. OpenXRActionSetDescriptor::Reflect(context);
  291. AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
  292. if (serialize)
  293. {
  294. serialize->Class<OpenXRActionSetsAsset, AZ::Data::AssetData>()
  295. ->Version(1)
  296. ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true)
  297. ->Field("InteractionProfilesAsset", &OpenXRActionSetsAsset::m_interactionProfilesAsset)
  298. ->Field("ActionSetDescriptors", &OpenXRActionSetsAsset::m_actionSetDescriptors)
  299. ;
  300. AZ::EditContext* edit = serialize->GetEditContext();
  301. if (edit)
  302. {
  303. edit->Class<OpenXRActionSetsAsset>(
  304. s_assetTypeName, "Defines the OpenXR Actions an application cares about.")
  305. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  306. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  307. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetsAsset::m_interactionProfilesAsset, "Interaction Profiles Asset", "This asset determines the hardware systems that are compatible with these Action Sets.")
  308. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionSetsAsset::OnInteractionProfilesAssetChanged)
  309. ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetsAsset::m_actionSetDescriptors, "Action Sets", "List of action sets.")
  310. ;
  311. }
  312. }
  313. }
  314. AZ::Crc32 OpenXRActionSetsAsset::OnInteractionProfilesAssetChanged()
  315. {
  316. EditorInternal::SetCurrentInteractionProfilesAsset(m_interactionProfilesAsset);
  317. return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
  318. }
  319. /// OpenXRActionBindingsAsset
  320. ///////////////////////////////////////////////////////////
  321. ///////////////////////////////////////////////////////////
  322. /// OpenXRActionBindingsAssetHandler
  323. OpenXRActionSetsAssetHandler::OpenXRActionSetsAssetHandler()
  324. : AzFramework::GenericAssetHandler<OpenXRActionSetsAsset>(
  325. OpenXRActionSetsAsset::s_assetTypeName,
  326. "Other",
  327. OpenXRActionSetsAsset::s_assetExtension)
  328. {
  329. }
  330. AZ::Data::AssetHandler::LoadResult OpenXRActionSetsAssetHandler::LoadAssetData(
  331. const AZ::Data::Asset<AZ::Data::AssetData>& asset,
  332. AZStd::shared_ptr<AZ::Data::AssetDataStream> stream,
  333. const AZ::Data::AssetFilterCB& assetLoadFilterCB)
  334. {
  335. auto actionSetsAsset = asset.GetAs<OpenXRActionSetsAsset>();
  336. if (!actionSetsAsset)
  337. {
  338. AZ_Error("OpenXRActionSetsAssetHandler", false, "This should be a OpenXRActionSetsAsset, as this is the only type we process.");
  339. return AZ::Data::AssetHandler::LoadResult::Error;
  340. }
  341. if (!AZ::Utils::LoadObjectFromStreamInPlace(
  342. *stream, *actionSetsAsset, nullptr, AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB)))
  343. {
  344. return AZ::Data::AssetHandler::LoadResult::Error;
  345. }
  346. // The reason we don't load the interaction profiles asset upon construction
  347. // is because we only want to load the singleton asset only if we are 100% sure
  348. // this asset is being edited by the Asset Editor.
  349. // Remember that at game runtime, there's no such thing as the Asset Editor.
  350. constexpr bool loadAsset = false;
  351. EditorInternal::SetCurrentInteractionProfilesAsset(actionSetsAsset->m_interactionProfilesAsset, loadAsset);
  352. return AZ::Data::AssetHandler::LoadResult::LoadComplete;
  353. }
  354. bool OpenXRActionSetsAssetHandler::SaveAssetData(const AZ::Data::Asset<AZ::Data::AssetData>& asset, AZ::IO::GenericStream* stream)
  355. {
  356. OpenXRActionSetsAsset* assetData = asset.GetAs<OpenXRActionSetsAsset>();
  357. if (!assetData->m_interactionProfilesAsset.GetId().IsValid())
  358. {
  359. AZ_Error("OpenXRActionSetsAssetHandler", false, "Can't save this OpenXRActionSetsAsset without a valid OpenXRInteractionProfilesAsset")
  360. return false;
  361. }
  362. if (!assetData->m_interactionProfilesAsset.IsReady())
  363. {
  364. EditorInternal::SetCurrentInteractionProfilesAsset(assetData->m_interactionProfilesAsset);
  365. }
  366. auto outcome = OpenXRVkAssetsValidator::ValidateActionSetsAsset(*assetData, *(assetData->m_interactionProfilesAsset.Get()));
  367. if (!outcome.IsSuccess())
  368. {
  369. AZ_Error("OpenXRActionSetsAssetHandler", false, "Can't save this OpenXRActionSetsAsset. Reason:\n%s", outcome.GetError().c_str());
  370. return false;
  371. }
  372. return AzFramework::GenericAssetHandler<OpenXRActionSetsAsset>::SaveAssetData(asset, stream);
  373. }
  374. /// OpenXRActionBindingsAssetHandler
  375. ///////////////////////////////////////////////////////////
  376. } // namespace OpenXRVk