From fa06090439c4711a36eb464c30f9aa4691ad24a3 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 5 Jan 2024 17:10:34 -0600 Subject: [PATCH 01/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRInteractionProviderBus.h | 31 +++ .../OpenXRVk/OpenXRVkSystemComponent.h | 3 + .../KHRSimpleProfileSystemComponent.cpp | 65 ++++++ .../KHRSimpleProfileSystemComponent.h | 53 +++++ .../Code/Source/OpenXRActionsBindingAsset.cpp | 201 ++++++++++++++++++ .../Code/Source/OpenXRActionsBindingAsset.h | 98 +++++++++ .../Code/Source/OpenXRVkSystemComponent.cpp | 29 +++ .../Code/openxrvk_private_common_files.cmake | 5 + 8 files changed, 485 insertions(+) create mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.h diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h new file mode 100644 index 000000000..26b4cc2a1 --- /dev/null +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +namespace OpenXRVk +{ + class OpenXRInteractionProvider + : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + typedef AZStd::string BusId; + + virtual AZStd::string GetName() const = 0; + virtual AZStd::vector GetUserPaths() const = 0; + virtual AZStd::vector GetComponentPaths(const AZStd::string& userPath) const = 0; + }; + + using OpenXRInteractionProviderBus = AZ::EBus; +} diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h index e980edeaf..b7dfa5491 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include "OpenXRActionsBindingAsset.h" namespace OpenXRVk { @@ -63,5 +65,6 @@ namespace OpenXRVk private: XR::Ptr m_instance; + AZStd::unique_ptr> m_actionsBindingAssetHandler; }; } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp new file mode 100644 index 000000000..cfaf7eb4d --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + + +#include + +#include "KHRSimpleProfileSystemComponent.h" + +namespace OpenXRVk +{ + void KHRSimpleProfileSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("InteractionProfileProviderService")); + } + + void KHRSimpleProfileSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1); + } + } + + void KHRSimpleProfileSystemComponent::Activate() + { + + } + + void KHRSimpleProfileSystemComponent::Deactivate() + { + if (m_instance) + { + XR::Factory::Unregister(this); + AZ::Interface::Unregister(m_instance.get()); + m_instance = nullptr; + } + + m_actionsBindingAssetHandler->Unregister(); + } + + /////////////////////////////////////////////////////////////////// + // OpenXRInteractionProviderBus::Handler overrides + //! Create OpenXRVk::Instance object + AZStd::string KHRSimpleProfileSystemComponent::GetName() const + { + return m_name; + } + + AZStd::vector KHRSimpleProfileSystemComponent::GetUserPaths() const + { + return m_userPaths; + } + + AZStd::vector KHRSimpleProfileSystemComponent::GetComponentPaths(const AZStd::string& userPath) const + { + + } + /////////////////////////////////////////////////////////////////// +} \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h new file mode 100644 index 000000000..9ff291ff8 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + + +namespace OpenXRVk +{ + //! This class is the component related to the vulkan backend of XR. + class KHRSimpleProfileSystemComponent final + : public AZ::Component + , public OpenXRInteractionProviderBus::Handler + { + public: + AZ_COMPONENT(KHRSimpleProfileSystemComponent, "{123EDAF5-416B-4AEF-BEEC-03A8A8C71643}"); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void Reflect(AZ::ReflectContext* context); + + KHRSimpleProfileSystemComponent() = default; + ~KHRSimpleProfileSystemComponent() = default; + + ////////////////////////////////////////////////////////////////////////// + // Component + void Activate() override; + void Deactivate() override; + ////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + // OpenXRInteractionProviderBus::Handler overrides + //! Create OpenXRVk::Instance object + AZStd::string GetName() const override; + AZStd::vector GetUserPaths() const override; + AZStd::vector GetComponentPaths(const AZStd::string& userPath) const override; + /////////////////////////////////////////////////////////////////// + + private: + AZStd::string m_name; + AZStd::vector m_userPaths; + //! The key is a user path and the value is a list of component paths that exist + //! for said user path. + AZStd::unordered_map> m_componentPaths; + }; +}//namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp new file mode 100644 index 000000000..4395125eb --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "OpenXRActionsBindingAsset.h" + +namespace OpenXRVk +{ + /////////////////////////////////////////////////////////// + /// OpenXRActionPath + void OpenXRActionPath::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Profile", &OpenXRActionPath::m_interactionProfile) + ->Field("UserPath", &OpenXRActionPath::m_userPath) + ->Field("ComponentPath", &OpenXRActionPath::m_componentPath) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRActionPath", "A specific OpenXR I/O action path.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionPath::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_interactionProfile, "Interaction Profile", "The Interaction I/O profile") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnInteractionProfileSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetInteractionProfiles) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_userPath, "User Path", "Root path identifier") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnUserPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetUserPaths) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_componentPath, "I/O Component Path", "I/O Component Path identifier") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnComponentPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetComponentPaths) + ; + } + } + } + + AZStd::string OpenXRActionPath::GetEditorText() const + { + return AZStd::string::format("%s-%s-%s", m_interactionProfile.c_str(), m_userPath.c_str(), m_componentPath.c_str()); + } + + AZ::Crc32 OpenXRActionPath::OnInteractionProfileSelected() + { + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZStd::vector OpenXRActionPath::GetInteractionProfiles() const + { + return {}; + } + + AZ::Crc32 OpenXRActionPath::OnUserPathSelected() + { + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZStd::vector OpenXRActionPath::GetUserPaths() const + { + return {}; + } + + AZ::Crc32 OpenXRActionPath::OnComponentPathSelected() + { + return AZ::Edit::PropertyRefreshLevels::None; + } + + AZStd::vector OpenXRActionPath::GetComponentPaths() const + { + return {}; + } + /// OpenXRActionPath + /////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////// + /// OpenXRAction + void OpenXRAction::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRAction::m_name) + ->Field("LocalizedName", &OpenXRAction::m_localizedName) + ->Field("ActionPaths", &OpenXRAction::m_actionPaths) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRAction", "An action bound to one or more OpenXR I/O Paths.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRAction::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_name, "Name", "Runtime identifier for this action.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_localizedName, "Localized Name", "User friendly display name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_actionPaths, "Action Paths", "List of action paths bound to this action") + ; + } + } + } + + AZStd::string OpenXRAction::GetEditorText() const + { + if (!m_localizedName.empty()) + { + return m_localizedName; + } + return m_name.empty() ? "" : m_name; + } + /// OpenXRAction + /////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////// + /// OpenXRActionSet + void OpenXRActionSet::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRActionSet::m_name) + ->Field("LocalizedName", &OpenXRActionSet::m_localizedName) + ->Field("Priority", &OpenXRActionSet::m_priority) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRActionSet", "A group of OpenXR Actions that can be selectively activated/deactivated at runtime.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionSet::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_name, "Name", "Runtime identifier for this action set.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_localizedName, "Localized Name", "Action set display name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSet::m_priority, "Priority", "The higher this value the higher the priority.") + ; + } + } + } + + AZStd::string OpenXRActionSet::GetEditorText() const + { + if (!m_localizedName.empty()) + { + return m_localizedName; + } + return m_name.empty() ? "" : m_name; + } + /// OpenXRActionSet + /////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////// + /// OpenXRActionBindingsAsset + void OpenXRActionBindingsAsset::Reflect(AZ::ReflectContext* context) + { + OpenXRActionSet::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) + ->Field("ActionSets", &OpenXRActionBindingsAsset::m_actionSets) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + s_assetTypeName, "Defines the OpenXR Actions an application cares about.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionBindingsAsset::m_actionSets, "Action Sets", "List of action sets.") + ; + } + } + } + /// OpenXRActionBindingsAsset + /////////////////////////////////////////////////////////// + +} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.h new file mode 100644 index 000000000..a2b6ade17 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace OpenXRVk +{ + class OpenXRActionPath final + { + public: + AZ_RTTI(OpenXRActionPath, "{F25D6382-C9E0-414B-A542-1758F5477D03}"); + virtual ~OpenXRActionPath() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + AZStd::string GetEditorText() const; + + AZStd::string m_interactionProfile; + AZStd::string m_userPath; + AZStd::string m_componentPath; + + private: + AZ::Crc32 OnInteractionProfileSelected(); + AZStd::vector GetInteractionProfiles() const; + + AZ::Crc32 OnUserPathSelected(); + AZStd::vector GetUserPaths() const; + + AZ::Crc32 OnComponentPathSelected(); + AZStd::vector GetComponentPaths() const; + }; + + class OpenXRAction final + { + public: + AZ_RTTI(OpenXRAction, "{90BBF6F6-C7D6-4F64-B784-CE03F86DC36B}"); + virtual ~OpenXRAction() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + AZStd::string GetEditorText() const; + + AZStd::string m_name; // Regular char* + AZStd::string m_localizedName; // UTF-8 string. + //! List of I/O action paths that will be bound to this action. + //! The first action path in this list, determines what type of action paths + //! can be added to the list. For example: + //! If the first action path happens to be a boolean, then subsequent action paths + //! can only be added if they can map to a boolean. + //! Another important case is if the this is a haptic feedback action (Output), then + //! subsequent action paths can only be of type haptic feedback actions. + AZStd::vector m_actionPaths; + }; + + class OpenXRActionSet final + { + public: + AZ_RTTI(OpenXRActionSet, "{3A08BC1F-656F-441F-89C3-829F95B9B329}"); + virtual ~OpenXRActionSet() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + AZStd::string GetEditorText() const; + + AZStd::string m_name; // Regular char* + AZStd::string m_localizedName; // UTF-8 string. + uint32_t m_priority = 0; // Higher values mean higher priority. + AZStd::vector m_actions; + }; + + //! This asset defines a list of OpenXR Action Sets that an application supports + //! regarding inputs and haptics. + class OpenXRActionBindingsAsset final + : public AZ::Data::AssetData + { + public: + AZ_RTTI(OpenXRActionBindingsAsset, "{C2DEE370-6151-4701-AEA5-AEA3CA247CFF}", AZ::Data::AssetData); + AZ_CLASS_ALLOCATOR(OpenXRActionBindingsAsset, AZ::SystemAllocator); + static void Reflect(AZ::ReflectContext* context); + + static constexpr char s_assetTypeName[] = "OpenXR Actions Binding Asset"; + static constexpr char s_assetExtension[] = "xractions"; + + AZStd::vector m_actionSets; + }; +}// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index 478074807..3846f1d52 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -37,6 +37,7 @@ namespace OpenXRVk ->Version(1); } + OpenXRActionBindingsAsset::Reflect(context); AzFramework::InputDeviceXRController::Reflect(context); } @@ -82,6 +83,9 @@ namespace OpenXRVk void SystemComponent::Activate() { + m_actionsBindingAssetHandler = AZStd::make_unique>(OpenXRActionBindingsAsset::s_assetTypeName, "Other", OpenXRActionBindingsAsset::s_assetExtension); + m_actionsBindingAssetHandler->Register(); + if (XR::IsOpenXREnabled()) { m_instance = AZStd::static_pointer_cast(CreateInstance()); @@ -110,5 +114,30 @@ namespace OpenXRVk AZ::Interface::Unregister(m_instance.get()); m_instance = nullptr; } + + m_actionsBindingAssetHandler->Unregister(); } + + // /////////////////////////////////////////////////////////////////// + // // AssetTypeInfoBus overrides + // AZ::Data::AssetType SystemComponent::GetAssetType() const + // { + // return AZ::AzTypeInfo::Uuid(); + // } + // + // const char* SystemComponent::GetAssetTypeDisplayName() const + // { + // return OpenXRActionBindingsAsset::s_assetTypeName; + // } + // + // void SystemComponent::GetAssetTypeExtensions(AZStd::vector& extensions) + // { + // extensions.push_back(OpenXRActionBindingsAsset::s_assetExtension); + // } + // + // bool SystemComponent::CanCreateComponent([[maybe_unused]] const AZ::Data::AssetId& assetId) const + // { + // return false; + // } + // /////////////////////////////////////////////////////////////////// } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index 78afced92..2c3f646dd 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -17,6 +17,7 @@ set(FILES Include/OpenXRVk/OpenXRVkSwapChain.h Include/OpenXRVk/OpenXRVkSystemComponent.h Include/OpenXRVk/OpenXRVkUtils.h + Include/OpenXRVk/OpenXRInteractionProviderBus.h Source/InputDeviceXRController.cpp Source/OpenXRVkCommon.h Source/OpenXRVkDevice.cpp @@ -28,6 +29,10 @@ set(FILES Source/OpenXRVkSwapChain.cpp Source/OpenXRVkSystemComponent.cpp Source/OpenXRVkUtils.cpp + Source/OpenXRActionsBindingAsset.cpp + Source/OpenXRActionsBindingAsset.h Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h + Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp + Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h ) From abafb9925f092fbb1c8befca8bac7e2f42b9c677 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sat, 6 Jan 2024 10:54:36 -0600 Subject: [PATCH 02/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRInteractionProviderBus.h | 15 +++++- .../KHRSimpleProfileSystemComponent.cpp | 54 +++++++++++++++---- .../KHRSimpleProfileSystemComponent.h | 13 +++-- .../Code/Source/OpenXRActionsBindingAsset.cpp | 36 ++++++++++--- Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp | 6 ++- 5 files changed, 100 insertions(+), 24 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h index 26b4cc2a1..ae9ca5b37 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h @@ -8,19 +8,32 @@ #pragma once +#include + #include #include #include namespace OpenXRVk { + struct OpenXRPath + { + AZStd::string m_displayName; + AZStd::string m_xrPath; + }; + + struct OpenXRComponentPath : public OpenXRPath + { + XrActionType m_actionType; + }; + class OpenXRInteractionProvider : public AZ::EBusTraits { public: static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; - typedef AZStd::string BusId; + typedef AZStd::string BusIdType; virtual AZStd::string GetName() const = 0; virtual AZStd::vector GetUserPaths() const = 0; diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp index cfaf7eb4d..0a53edc0a 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp @@ -29,19 +29,32 @@ namespace OpenXRVk void KHRSimpleProfileSystemComponent::Activate() { - + m_name = { + "Khronos Simple Interaction Profile", + "/interaction_profiles/khr/simple_controller" + }; + + m_userPaths = { + {LeftHand, "/user/hand/left"}, + {RightHand, "/user/hand/right"} + }; + + const AZStd::vector commonPaths = { + {"Select Button", "/input/select/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"Menu Button", "/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, + {"Grip", "/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"Aim", "/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"Vibration", "/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + }; + m_componentPaths[LeftHand] = commonPaths; + m_componentPaths[RightHand] = commonPaths; + + OpenXRInteractionProviderBus::Handler::BusConnect(m_name.m_displayName); } void KHRSimpleProfileSystemComponent::Deactivate() { - if (m_instance) - { - XR::Factory::Unregister(this); - AZ::Interface::Unregister(m_instance.get()); - m_instance = nullptr; - } - - m_actionsBindingAssetHandler->Unregister(); + OpenXRInteractionProviderBus::Handler::BusDisconnect(); } /////////////////////////////////////////////////////////////////// @@ -49,17 +62,36 @@ namespace OpenXRVk //! Create OpenXRVk::Instance object AZStd::string KHRSimpleProfileSystemComponent::GetName() const { - return m_name; + return m_name.m_displayName; } AZStd::vector KHRSimpleProfileSystemComponent::GetUserPaths() const { - return m_userPaths; + AZStd::vector retList; + retList.reserve(m_userPaths.size()); + for (const auto& pathTuple : m_userPaths) + { + retList.push_back(pathTuple.m_displayName); + } + return retList; } AZStd::vector KHRSimpleProfileSystemComponent::GetComponentPaths(const AZStd::string& userPath) const { + if (!m_componentPaths.contains(userPath)) + { + AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); + return {}; + } + const auto& paths = m_componentPaths.at(userPath); + AZStd::vector retList; + retList.reserve(paths.size()); + for (const auto& pathTuple : paths) + { + retList.push_back(pathTuple.m_displayName); + } + return retList; } /////////////////////////////////////////////////////////////////// } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h index 9ff291ff8..8aa13f056 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h @@ -15,7 +15,8 @@ namespace OpenXRVk { - //! This class is the component related to the vulkan backend of XR. + //! This system component provides data that can be used to pick xrActions + //! that will used at runtime for any given application. class KHRSimpleProfileSystemComponent final : public AZ::Component , public OpenXRInteractionProviderBus::Handler @@ -44,10 +45,14 @@ namespace OpenXRVk /////////////////////////////////////////////////////////////////// private: - AZStd::string m_name; - AZStd::vector m_userPaths; + static constexpr char LogName[] = "KHRSimpleProfileSystemComponent"; + + static constexpr AZStd::string_view LeftHand = "(L)"; + static constexpr AZStd::string_view RightHand = "(R)"; + OpenXRPath m_name; + AZStd::vector m_userPaths; //! The key is a user path and the value is a list of component paths that exist //! for said user path. - AZStd::unordered_map> m_componentPaths; + AZStd::unordered_map> m_componentPaths; }; }//namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp index 4395125eb..163e22cf8 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp @@ -6,6 +6,7 @@ * */ +#include #include "OpenXRActionsBindingAsset.h" namespace OpenXRVk @@ -47,37 +48,52 @@ namespace OpenXRVk AZStd::string OpenXRActionPath::GetEditorText() const { - return AZStd::string::format("%s-%s-%s", m_interactionProfile.c_str(), m_userPath.c_str(), m_componentPath.c_str()); + return AZStd::string::format("%s %s %s", m_interactionProfile.c_str(), m_userPath.c_str(), m_componentPath.c_str()); } AZ::Crc32 OpenXRActionPath::OnInteractionProfileSelected() { - return AZ::Edit::PropertyRefreshLevels::None; + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } AZStd::vector OpenXRActionPath::GetInteractionProfiles() const { - return {}; + AZStd::vector retList; + + OpenXRInteractionProviderBus::EnumerateHandlers([&retList](OpenXRInteractionProvider* handler) -> bool { + retList.push_back(handler->GetName()); + return true; + }); + + return retList; } AZ::Crc32 OpenXRActionPath::OnUserPathSelected() { - return AZ::Edit::PropertyRefreshLevels::None; + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } AZStd::vector OpenXRActionPath::GetUserPaths() const { - return {}; + AZStd::vector retList; + + OpenXRInteractionProviderBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProvider::GetUserPaths); + + return retList; } AZ::Crc32 OpenXRActionPath::OnComponentPathSelected() { - return AZ::Edit::PropertyRefreshLevels::None; + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } AZStd::vector OpenXRActionPath::GetComponentPaths() const { - return {}; + AZStd::vector retList; + + OpenXRInteractionProviderBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProvider::GetComponentPaths, m_userPath); + + return retList; } /// OpenXRActionPath /////////////////////////////////////////////////////////// @@ -87,6 +103,8 @@ namespace OpenXRVk /// OpenXRAction void OpenXRAction::Reflect(AZ::ReflectContext* context) { + OpenXRActionPath::Reflect(context); + AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { @@ -129,6 +147,8 @@ namespace OpenXRVk /// OpenXRActionSet void OpenXRActionSet::Reflect(AZ::ReflectContext* context) { + OpenXRAction::Reflect(context); + AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { @@ -137,6 +157,7 @@ namespace OpenXRVk ->Field("Name", &OpenXRActionSet::m_name) ->Field("LocalizedName", &OpenXRActionSet::m_localizedName) ->Field("Priority", &OpenXRActionSet::m_priority) + ->Field("Actions", &OpenXRActionSet::m_actions) ; AZ::EditContext* edit = serialize->GetEditContext(); @@ -151,6 +172,7 @@ namespace OpenXRVk ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_localizedName, "Localized Name", "Action set display name.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSet::m_priority, "Priority", "The higher this value the higher the priority.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_actions, "Actions", "List of actions for this action set.") ; } } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp index 3db92e124..542c1096a 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp @@ -8,8 +8,10 @@ #include #include + #include #include +#include "InteractionProfiles/KHRSimpleProfileSystemComponent.h" namespace OpenXRVk { @@ -27,6 +29,7 @@ namespace OpenXRVk m_descriptors.insert(m_descriptors.end(), { SystemComponent::CreateDescriptor(), XRCameraMovementComponent::CreateDescriptor(), + KHRSimpleProfileSystemComponent::CreateDescriptor(), }); } @@ -34,7 +37,8 @@ namespace OpenXRVk { return { - azrtti_typeid() + azrtti_typeid(), + azrtti_typeid(), }; } }; From 2b9150e6d914185ff76e50b99beecef21379df7b Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:15:36 -0600 Subject: [PATCH 03/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Include/OpenXRVk/OpenXRActionsInterface.h | 54 + .../OpenXRVk/OpenXRInteractionProfileBus.h | 59 + .../OpenXRVk/OpenXRInteractionProviderBus.h | 44 - .../Code/Include/OpenXRVk/OpenXRVkSession.h | 4 + .../Code/Include/OpenXRVk/OpenXRVkUtils.h | 1 + .../KHRSimpleProfileSystemComponent.cpp | 50 +- .../KHRSimpleProfileSystemComponent.h | 26 +- .../Code/Source/OpenXRActionsBindingAsset.cpp | 8 +- .../Code/Source/OpenXRActionsManager.cpp | 1007 +++++++++++++++++ .../Code/Source/OpenXRActionsManager.h | 117 ++ Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 19 +- Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp | 11 + .../Code/openxrvk_private_common_files.cmake | 5 +- Projects/OpenXRTest/Registry/OpenXR.setreg | 8 +- Projects/OpenXRTest/project.json | 4 +- 15 files changed, 1352 insertions(+), 65 deletions(-) create mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h create mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h delete mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h new file mode 100644 index 000000000..1d5fa53ce --- /dev/null +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include + +#include + +namespace OpenXRVk +{ + + //! Interface used to query the state of actions and also + //! to drive the state of haptic feedback actions. + //! The implementation is encouraged to expose each method + //! of this interface as global functions in the behavior context. + class IOpenXRActions + { + public: + AZ_RTTI(IOpenXRActions, "{7B163790-BDBE-4C5B-832E-768CF5CDF585}"); + AZ_DISABLE_COPY_MOVE(IOpenXRActions); + + IOpenXRActions() = default; + virtual ~IOpenXRActions() = default; + + using ActionHandle = AZ::RHI::Handle; + + virtual AZStd::vector GetAllActionSets() const = 0; + virtual AZStd::vector GetActiveActionSets() const = 0; + virtual AZStd::vector GetInactiveActionSets() const = 0; + virtual AZ::Outcome ChangeActionSetState(const AZStd::string& actionSetName, bool activate) = 0; + virtual AZ::Outcome ChangeActionSetsState(const AZStd::vector& actionSetNames, bool activate) = 0; + + virtual ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const = 0; + + virtual AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) = 0; + virtual AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) = 0; + virtual AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) = 0; + virtual AZ::Outcome GetActionStatePose(ActionHandle actionHandle) = 0; + + virtual AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) = 0; + virtual AZ::Outcome StopHapticVibrationAction(ActionHandle actionHandle) = 0; + }; + + using OpenXRActionsInterface = AZ::Interface; + +} // namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h new file mode 100644 index 000000000..d172d9f9a --- /dev/null +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include +#include + +namespace OpenXRVk +{ + class OpenXRInteractionProfile + : public AZ::EBusTraits + { + public: + static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; + static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; + // The bus ID is OpenXRActionPath::m_interactionProfile. + // Should be the exact same string as returned by GetName() + typedef AZStd::string BusIdType; + + ////////////////////////////////////////////////////////// + // The following functions are called during asset creation time. + virtual AZStd::string GetName() const = 0; + virtual AZStd::vector GetUserPaths() const = 0; + virtual AZStd::vector GetComponentPaths(const AZStd::string& userPath) const = 0; + // + ////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////// + // The following functions are called at runtime. + // + struct ActionPathInfo + { + //! Absolute path looks like: + //! /user/hand/left/input/select/click + AZStd::string m_absolutePath; + XrActionType m_actionType = XrActionType::XR_ACTION_TYPE_MAX_ENUM; + }; + + //! @param userPath Typically comes from OpenXRActionPath::m_userPath. + //! @param componentPath Typically comes from OpenXRActionPath::m_componentPath. + virtual ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const = 0; + + virtual AZStd::string GetInteractionProviderPath() const = 0; + // + ///////////////////////////////////////////////////////////// + }; + + using OpenXRInteractionProfileBus = AZ::EBus; +} diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h deleted file mode 100644 index ae9ca5b37..000000000 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProviderBus.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -#include -#include -#include - -namespace OpenXRVk -{ - struct OpenXRPath - { - AZStd::string m_displayName; - AZStd::string m_xrPath; - }; - - struct OpenXRComponentPath : public OpenXRPath - { - XrActionType m_actionType; - }; - - class OpenXRInteractionProvider - : public AZ::EBusTraits - { - public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; - typedef AZStd::string BusIdType; - - virtual AZStd::string GetName() const = 0; - virtual AZStd::vector GetUserPaths() const = 0; - virtual AZStd::vector GetComponentPaths(const AZStd::string& userPath) const = 0; - }; - - using OpenXRInteractionProviderBus = AZ::EBus; -} diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h index a6886b685..ca8da5afc 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h @@ -15,6 +15,8 @@ namespace OpenXRVk { + class ActionsManager; + // Class that will help manage XrSession class Session final : public XR::Session @@ -93,6 +95,8 @@ namespace OpenXRVk XrEventDataBuffer m_eventDataBuffer; XrInstance m_xrInstance = XR_NULL_HANDLE; XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR }; + + AZStd::unique_ptr m_actionsMgr; // Application defined base space that will used to calculate // the relative pose of all other spaces. diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h index f0c52d11a..21e0a0b67 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h @@ -59,6 +59,7 @@ namespace OpenXRVk bool IsSuccess(XrResult result); bool IsError(XrResult result); const char* GetResultString(const XrResult result); + void PrintXrError(const char* windowName, const XrResult error, const char* fmt, ...); XR::RawStringList FilterList(const XR::RawStringList& source, const XR::StringList& filter); //! Input is an array of chars with multiple ' ' char embedded in it, indicating the start of a new string. diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp index 0a53edc0a..9ef15ae3b 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp @@ -30,7 +30,7 @@ namespace OpenXRVk void KHRSimpleProfileSystemComponent::Activate() { m_name = { - "Khronos Simple Interaction Profile", + "Khronos Simple", // Khronos Simple Interaction Profile "/interaction_profiles/khr/simple_controller" }; @@ -42,23 +42,23 @@ namespace OpenXRVk const AZStd::vector commonPaths = { {"Select Button", "/input/select/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, {"Menu Button", "/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, - {"Grip", "/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, - {"Aim", "/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"Grip Pose", "/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, + {"Aim Pose", "/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, {"Vibration", "/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, }; m_componentPaths[LeftHand] = commonPaths; m_componentPaths[RightHand] = commonPaths; - OpenXRInteractionProviderBus::Handler::BusConnect(m_name.m_displayName); + OpenXRInteractionProfileBus::Handler::BusConnect(m_name.m_displayName); } void KHRSimpleProfileSystemComponent::Deactivate() { - OpenXRInteractionProviderBus::Handler::BusDisconnect(); + OpenXRInteractionProfileBus::Handler::BusDisconnect(); } /////////////////////////////////////////////////////////////////// - // OpenXRInteractionProviderBus::Handler overrides + // OpenXRInteractionProfileBus::Handler overrides //! Create OpenXRVk::Instance object AZStd::string KHRSimpleProfileSystemComponent::GetName() const { @@ -93,5 +93,43 @@ namespace OpenXRVk } return retList; } + + OpenXRInteractionProfile::ActionPathInfo KHRSimpleProfileSystemComponent::GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const + { + OpenXRInteractionProfile::ActionPathInfo retPathInfo; + + const auto* openxrUserPath = AZStd::find_if(m_userPaths.begin(), m_userPaths.end(), + [userPath](const OpenXRPath& entry) { + return entry.m_displayName == userPath; + }); + + if (openxrUserPath == m_userPaths.end()) + { + AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); + return retPathInfo; + } + + const auto& componentPaths = m_componentPaths.at(userPath); + const auto* openxrComponentPath = AZStd::find_if(componentPaths.begin(), componentPaths.end(), + [componentPath](const OpenXRComponentPath& entry) { + return entry.m_displayName == componentPath; + }); + + if (openxrComponentPath == componentPaths.end()) + { + AZ_Error(LogName, false, "Invalid component path [%s] for user path [%s].\n", componentPath.c_str(), userPath.c_str()); + return retPathInfo; + } + + retPathInfo.m_actionType = openxrComponentPath->m_actionType; + retPathInfo.m_absolutePath = openxrUserPath->m_xrRelativePath + openxrComponentPath->m_xrRelativePath; + + return retPathInfo; + } + + AZStd::string KHRSimpleProfileSystemComponent::GetInteractionProviderPath() const + { + return m_name.m_xrRelativePath; + } /////////////////////////////////////////////////////////////////// } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h index 8aa13f056..f86911020 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h @@ -10,16 +10,34 @@ #include -#include +#include namespace OpenXRVk { + struct OpenXRPath + { + AZStd::string m_displayName; + //! Although this path is relative, + //! it should start with "/". + //! Examples: + //! 1- For a User Path this string would look like this: + //! "/user/hand/left" + //! 2- For a Component Path this string would look like this: + //! "/input/select/click" + AZStd::string m_xrRelativePath; + }; + + struct OpenXRComponentPath : public OpenXRPath + { + XrActionType m_actionType; + }; + //! This system component provides data that can be used to pick xrActions //! that will used at runtime for any given application. class KHRSimpleProfileSystemComponent final : public AZ::Component - , public OpenXRInteractionProviderBus::Handler + , public OpenXRInteractionProfileBus::Handler { public: AZ_COMPONENT(KHRSimpleProfileSystemComponent, "{123EDAF5-416B-4AEF-BEEC-03A8A8C71643}"); @@ -37,11 +55,13 @@ namespace OpenXRVk ////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////// - // OpenXRInteractionProviderBus::Handler overrides + // OpenXRInteractionProfileBus::Handler overrides //! Create OpenXRVk::Instance object AZStd::string GetName() const override; AZStd::vector GetUserPaths() const override; AZStd::vector GetComponentPaths(const AZStd::string& userPath) const override; + OpenXRInteractionProfile::ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const override; + AZStd::string GetInteractionProviderPath() const override; /////////////////////////////////////////////////////////////////// private: diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp index 163e22cf8..350a281e4 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp @@ -6,7 +6,7 @@ * */ -#include +#include #include "OpenXRActionsBindingAsset.h" namespace OpenXRVk @@ -60,7 +60,7 @@ namespace OpenXRVk { AZStd::vector retList; - OpenXRInteractionProviderBus::EnumerateHandlers([&retList](OpenXRInteractionProvider* handler) -> bool { + OpenXRInteractionProfileBus::EnumerateHandlers([&retList](OpenXRInteractionProfile* handler) -> bool { retList.push_back(handler->GetName()); return true; }); @@ -77,7 +77,7 @@ namespace OpenXRVk { AZStd::vector retList; - OpenXRInteractionProviderBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProvider::GetUserPaths); + OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetUserPaths); return retList; } @@ -91,7 +91,7 @@ namespace OpenXRVk { AZStd::vector retList; - OpenXRInteractionProviderBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProvider::GetComponentPaths, m_userPath); + OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetComponentPaths, m_userPath); return retList; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp new file mode 100644 index 000000000..17cc725b5 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -0,0 +1,1007 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +//#include +#include + +//#include +//#include +//#include +//#include +//#include +#include "OpenXRActionsBindingAsset.h" +#include +#include +#include "OpenXRActionsManager.h" + +namespace OpenXRVk +{ + // XR::Ptr Action::Create() + // { + // const auto newInput = aznew Action; + // //newInput->m_xrController.SetImplementation(&AzFramework::InputDeviceXRController::Implementation::Create); + // //newInput->m_xrControllerImpl = newInput->m_xrController.GetImplementation(); + // return newInput; + // } + + bool ActionsManager::Init(XrInstance xrInstance, XrSession xrSession) + { + m_xrInstance = xrInstance; + m_xrSession = xrSession; + + // OpenXR only allows to define ActionSets during session creation. + // From the point of view of O3DE, the developer defines action sets + // in an asset of type OpenXRActionBindingsAsset. + // The default source path for said asset is "@project@/openxr.xractions". + const auto actionsBindingAsset = AZ::RPI::AssetUtils::LoadCriticalAsset({ DefaultActionsAssetPath }); + if (!actionsBindingAsset.IsReady()) + { + AZ_Printf(LogName, "This application won't support user interactions. Default action bindings asset [%s] not found.\n", DefaultActionsAssetPath); + return true; + } + + AZStd::unordered_set activeProfiles; + AZStd::vector activeBindings; + for (const auto& actionSet : actionsBindingAsset->m_actionSets) + { + if (!InitActionSetInternal(actionSet, activeProfiles, activeBindings)) + { + return false; + } + } + + if (activeBindings.empty() || activeProfiles.empty()) + { + AZ_Printf(LogName, "This application will run without actions.\n"); + return true; + } + + // Register the bindings for each active interaction profile. + for (const auto& profilePath : activeProfiles) + { + XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING }; + suggestedBindings.interactionProfile = profilePath; + suggestedBindings.suggestedBindings = activeBindings.data(); + suggestedBindings.countSuggestedBindings = static_cast(activeBindings.size()); + XrResult result = xrSuggestInteractionProfileBindings(m_xrInstance, &suggestedBindings); + WARN_IF_UNSUCCESSFUL(result); + } + + AZStd::vector xrActionSets; + xrActionSets.reserve(m_actionSets.size()); + size_t actionSetIdx = 0; + for (const auto& actionSetInfo : m_actionSets) + { + xrActionSets.push_back(actionSetInfo.m_xrActionSet); + m_activeActionSets.set(actionSetIdx, true); // By default all actionSets will be active. + actionSetIdx++; + } + RecreateXrActiveActionSets(); + + XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO }; + attachInfo.countActionSets = static_cast(xrActionSets.size()); + attachInfo.actionSets = xrActionSets.data(); + XrResult result = xrAttachSessionActionSets(m_xrSession, &attachInfo); + if (IsError(result)) + { + m_xrActiveActionSets.clear(); + PrintXrError(LogName, result, "Failed to attach %zu action sets to the session.", xrActionSets.size()); + return false; + } + + return true; + } + + bool ActionsManager::SyncActions() + { + if (m_xrActiveActionSets.empty()) + { + // Nothing to do + return true; + } + + XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO }; + syncInfo.countActiveActionSets = aznumeric_cast(m_xrActiveActionSets.size()); + syncInfo.activeActionSets = m_xrActiveActionSets.data(); + XrResult result = xrSyncActions(m_xrSession, &syncInfo); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to sync %zu actionSets.\n", m_xrActiveActionSets.size()); + return false; + } + + return true; + } + + + + bool ActionsManager::InitActionSetInternal(const OpenXRActionSet& actionSet, + AZStd::unordered_set& activeProfiles, + AZStd::vector& activeBindings) + { + // Create an action set. + XrActionSetCreateInfo actionSetCreateInfo{}; + actionSetCreateInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO; + azstrcpy(actionSetCreateInfo.actionSetName, sizeof(actionSetCreateInfo.actionSetName), actionSet.m_name.c_str()); + azstrcpy(actionSetCreateInfo.localizedActionSetName, sizeof(actionSetCreateInfo.localizedActionSetName), actionSet.m_localizedName.c_str()); + actionSetCreateInfo.priority = actionSet.m_priority; + + m_actionSets.push_back({}); + ActionSetInfo& actionSetInfo = m_actionSets.back(); + XrResult result = xrCreateActionSet(m_xrInstance, &actionSetInfo, &actionSetInfo.m_xrActionSet); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to instantiate actionSet named [%s].", actionSet.m_name.c_str()); + return false; + } + + for (const auto& action : actionSet.m_actions) + { + if (!InitActionBindingsInternal(actionSetInfo, action, activeProfiles, activeBindings)) + { + AZ_Error(LogName, false, "Failed to created action named [%s] under actionSet named [%s].", + action.m_name.c_str(), actionSet.m_name.c_str()); + return false; + } + } + + return true; + } + + bool ActionsManager::InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, + AZStd::unordered_set& activeProfiles, + AZStd::vector& activeBindings) + { + // One OpenXRAction object contains a list of XrActions that need to be created. + // The action type for each XrAction will be the same and it will be determined by + // the action type of the first action in the list. + AZ_Assert(!action.m_actionPaths.empty(), "OpenXR Actions list must contain at least one action."); + const auto& firstAction = action.m_actionPaths[0]; + + auto interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(firstAction.m_interactionProfile); + if (!interactionProviderIface) + { + AZ_Error(LogName, false, "Couldn't find interaction data provider with id [%s].", firstAction.m_interactionProfile.c_str()) + return false; + } + + const auto firstActionInfo = interactionProviderIface->GetActionPathInfo(firstAction.m_userPath, firstAction.m_componentPath); + + XrActionCreateInfo actionCreateInfo{}; + actionCreateInfo.type = XR_TYPE_ACTION_CREATE_INFO; + actionCreateInfo.actionType = firstActionInfo.m_actionType; + azstrcpy(actionCreateInfo.actionName, sizeof(actionCreateInfo.actionName), action.m_name.c_str()); + azstrcpy(actionCreateInfo.localizedActionName, sizeof(actionCreateInfo.localizedActionName), action.m_localizedName.c_str()); + actionCreateInfo.countSubactionPaths = 0; // Subactions are not supported. + actionCreateInfo.subactionPaths = nullptr; // Subactions are not supported. + + XrAction newXrAction; + XrResult result = xrCreateAction(actionSetInfo.m_xrActionSet, &actionCreateInfo, &newXrAction); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to create action named %s.\n", action.m_name.c_str()); + return false; + } + + // For each actionPath in the list, create the XrPath and its binding. + uint32_t additionalBindingsCount = 0; + for (const auto& actionPath : action.m_actionPaths) + { + interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(actionPath.m_interactionProfile); + if (!interactionProviderIface) + { + AZ_Error(LogName, false, "Couldn't find interaction data provider with id [%s].", actionPath.m_interactionProfile.c_str()) + return false; + } + + const auto pathInfo = interactionProviderIface->GetActionPathInfo(actionPath.m_userPath, actionPath.m_componentPath); + if (pathInfo.m_absolutePath.empty()) + { + AZ_Warning(LogName, false, "Failed to retrieve action path info for profile [%s], user path [%s], component path [%s].\n", + actionPath.m_interactionProfile.c_str(), actionPath.m_userPath.c_str(), actionPath.m_componentPath.c_str()); + continue; + } + + XrPath xrBindingPath; + result = xrStringToPath(m_xrInstance, pathInfo.m_absolutePath.c_str(), &xrBindingPath); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to create XrPath for action with profile [%s], absolute path [%s].\n", + actionPath.m_interactionProfile.c_str(), pathInfo.m_absolutePath.c_str()); + continue; + } + + auto interactionProfilePathStr = interactionProviderIface->GetInteractionProviderPath(); + XrPath xrProviderPath; + result = xrStringToPath(m_xrInstance, interactionProfilePathStr.c_str(), &xrProviderPath); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to create XrPath for action provider [%s], provider path [%s].\n", + actionPath.m_interactionProfile.c_str(), interactionProfilePathStr.c_str()); + continue; + } + activeProfiles.emplace(xrProviderPath); + + XrActionSuggestedBinding binding; + binding.action = newXrAction; + binding.binding = xrBindingPath; + activeBindings.push_back(binding); + additionalBindingsCount++; + } + + if (additionalBindingsCount < 1) + { + // This action has no bindings. Remove it. + AZ_Warning(LogName, false, "The action [] had no bindings!.\n", action.m_name.c_str()); + xrDestroyAction(newXrAction); + return true; + } + + m_xrActions.push_back(newXrAction); + uint16_t newActionIndex = aznumeric_cast(m_xrActions.size() - 1); + + ActionInfo newActionInfo; + newActionInfo.m_name = action.m_name; + newActionInfo.m_actionType = firstActionInfo.m_actionType; + newActionInfo.m_actionHandle = IOpenXRActions::ActionHandle(newActionIndex); + actionSetInfo.m_actions.emplace(action.m_name, AZStd::move(newActionInfo)); + + return true; + } + + ///////////////////////////////////////////////// + /// OpenXRActionsInterface overrides + AZStd::vector ActionsManager::GetAllActionSets() const + { + AZStd::vector retList; + retList.reserve(m_actionSets.size()); + for (const auto& actionSetInfo : m_actionSets) + { + retList.push_back(actionSetInfo.m_name); + } + return retList; + } + + + AZStd::vector ActionsManager::GetActiveActionSets() const + { + AZStd::vector retList; + retList.reserve(m_activeActionSets.count()); + for (size_t i = 0; i < m_actionSets.size(); i++) + { + if (m_activeActionSets[i]) + { + retList.push_back(m_actionSets[i].m_name); + } + } + return retList; + } + + + AZStd::vector ActionsManager::GetInactiveActionSets() const + { + AZStd::vector retList; + retList.reserve(m_actionSets.size() - m_activeActionSets.count()); + for (size_t i = 0; i < m_actionSets.size(); i++) + { + if (!m_activeActionSets[i]) + { + retList.push_back(m_actionSets[i].m_name); + } + } + return retList; + } + + + AZ::Outcome ActionsManager::ChangeActionSetState(const AZStd::string& actionSetName, bool activate) + { + constexpr bool recreateXrActiveActionSets = true; + return ChangeActionSetStateInternal(actionSetName, activate, recreateXrActiveActionSets); + } + + + AZ::Outcome ActionsManager::ChangeActionSetsState(const AZStd::vector& actionSetNames, bool activate) + { + constexpr bool recreateXrActiveActionSets = false; + for (const auto& actionSetName : actionSetNames) + { + auto outcome = ChangeActionSetStateInternal(actionSetName, activate, recreateXrActiveActionSets); + if (!outcome.IsSuccess()) + { + return outcome; + } + } + RecreateXrActiveActionSets(); + return AZ::Success(true); + } + + + IOpenXRActions::ActionHandle ActionsManager::GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const + { + for (const auto& actionSetInfo : m_actionSets) + { + if (actionSetInfo.m_name != actionSetName) + { + continue; + } + const auto itor = actionSetInfo.m_actions.find(actionName); + if (itor == actionSetInfo.m_actions.end()) + { + return IOpenXRActions::ActionHandle::Null; + } + return itor->second.m_actionHandle; + } + return IOpenXRActions::ActionHandle::Null; + } + + AZ::Outcome ActionsManager::GetActionStateBoolean(ActionHandle actionHandle) + { + if (!actionHandle.IsValid()) + { + return AZ::Failure("Invalid actionHandle!"); + } + const auto actionIndex = actionHandle.GetIndex(); + + XrActionStateBoolean state { XR_TYPE_ACTION_STATE_BOOLEAN }; + XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; + getInfo.action = m_xrActions[actionIndex]; + XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state)); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + + return AZ::Success(state.currentState); + } + + AZ::Outcome ActionsManager::GetActionStateFloat(ActionHandle actionHandle) + { + if (!actionHandle.IsValid()) + { + return AZ::Failure("Invalid actionHandle!"); + } + const auto actionIndex = actionHandle.GetIndex(); + + XrActionStateFloat state{ XR_TYPE_ACTION_STATE_FLOAT }; + XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; + getInfo.action = m_xrActions[actionIndex]; + XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state)); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + + return AZ::Success(state.currentState); + } + + AZ::Outcome ActionsManager::GetActionStateVector2(ActionHandle actionHandle) + { + if (!actionHandle.IsValid()) + { + return AZ::Failure("Invalid actionHandle!"); + } + const auto actionIndex = actionHandle.GetIndex(); + + XrActionStateVector2f state{ XR_TYPE_ACTION_STATE_VECTOR2F }; + XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; + getInfo.action = m_xrActions[actionIndex]; + XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state)); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + + return AZ::Success(AZ::Vector2(state.currentState.x, state.currentState.y)); + } + + AZ::Outcome ActionsManager::GetActionStatePose(ActionHandle actionHandle) + { + //FIXME! + if (!actionHandle.IsValid()) + { + return AZ::Failure("Invalid actionHandle!"); + } + [[maybe_unused]] const auto actionIndex = actionHandle.GetIndex(); + return AZ::Success(AZ::Transform::CreateIdentity()); + } + + + AZ::Outcome ActionsManager::ApplyHapticVibrationAction(ActionHandle actionHandle, + uint64_t durationNanos, float frequencyHz, float amplitude) + { + if (!actionHandle.IsValid()) + { + return AZ::Failure("Invalid actionHandle!"); + } + const auto actionIndex = actionHandle.GetIndex(); + + // fire haptics using output action + XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION }; + vibration.amplitude = AZStd::clamp(amplitude, 0.0f, 1.0f); + vibration.duration = durationNanos; + vibration.frequency = frequencyHz; + XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO }; + hapticActionInfo.action = m_xrActions[actionIndex]; + XrResult result = xrApplyHapticFeedback(m_xrSession, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + return AZ::Success(true); + } + + AZ::Outcome ActionsManager::StopHapticVibrationAction(ActionHandle actionHandle) + { + if (!actionHandle.IsValid()) + { + return AZ::Failure("Invalid actionHandle!"); + } + const auto actionIndex = actionHandle.GetIndex(); + + // fire haptics using output action + XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO }; + hapticActionInfo.action = m_xrActions[actionIndex]; + XrResult result = xrStopHapticFeedback(m_xrSession, &hapticActionInfo); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + return AZ::Success(true); + } + /// OpenXRActionsInterface overrides + ///////////////////////////////////////////////// + AZ::Outcome ActionsManager::ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false) + { + // First get the index. + size_t foundIdx = 0; + for (const auto& actionSetInfo : m_actionSets) + { + if (actionSetInfo.m_name == actionSetName) + { + break; + } + foundIdx++; + } + + if (foundIdx >= m_actionSets.size()) + { + return AZ::Failure(AZStd::string::format( + "ActionSet with name [%s] not found.", actionSetName.c_str())); + } + + m_activeActionSets.set(foundIdx, activate); + + if (recreateXrActiveActionSets) + { + RecreateXrActiveActionSets(); + } + + return AZ::Success(true); + } + + void ActionsManager::RecreateXrActiveActionSets() + { + // Recreate and cache the XrActionActionSet list. + m_xrActiveActionSets.clear(); + for (size_t i = 0; i < m_actionSets.size(); i++) + { + if (m_activeActionSets[i]) + { + XrActiveActionSet activeActionSet{ m_actionSets[i].m_xrActionSet, XR_NULL_PATH }; + m_xrActiveActionSets.push_back(activeActionSet); + } + } + } + + // + // + // void Action::CreateAllActions(const XrInstance& xrInstance) + // { + // // Get the XrPath for the left and right hands - we will use them as subaction paths. + // const AZStd::string leftHandPath{ m_xrControllerImpl->GetLeftHandSubPath() }; + // const AZStd::string rightHandPath{ m_xrControllerImpl->GetRightHandSubPath() }; + // + // XrResult result = xrStringToPath(xrInstance, leftHandPath.data(), &m_handSubactionPath[static_cast(XR::Side::Left)]); + // WARN_IF_UNSUCCESSFUL(result); + // result = xrStringToPath(xrInstance, rightHandPath.data(), &m_handSubactionPath[static_cast(XR::Side::Right)]); + // WARN_IF_UNSUCCESSFUL(result); + // + // // Lambda to create an action and path, store them in m_xrActionPaths + // using namespace AzFramework; + // auto createXrAction = [this, &xrInstance](const InputChannelId& channelId, const XrActionType actionType) + // { + // m_xrActionIndices[channelId] = m_xrActionPaths.size(); + // m_xrActionPaths.push_back({}); + // + // CreateAction(m_xrActionPaths.back().action, actionType, channelId.GetName(), channelId.GetName(), + // aznumeric_cast(AZStd::size(m_handSubactionPath)), m_handSubactionPath.data()); + // + // const AZStd::string xrPathStr{ m_xrControllerImpl->GetInputChannelPath(channelId) }; + // [[maybe_unused]] const XrResult pathResult = xrStringToPath(xrInstance, xrPathStr.data(), &m_xrActionPaths.back().binding); + // WARN_IF_UNSUCCESSFUL(pathResult); + // }; + // + // for (const InputChannelId& channelId : InputDeviceXRController::Button::All) + // { + // createXrAction(channelId, XR_ACTION_TYPE_BOOLEAN_INPUT); + // } + // + // for (const InputChannelId& channelId : InputDeviceXRController::Trigger::All) + // { + // createXrAction(channelId, XR_ACTION_TYPE_FLOAT_INPUT); + // } + // + // for (const InputChannelId& channelId : InputDeviceXRController::ThumbStickAxis1D::All) + // { + // createXrAction(channelId, XR_ACTION_TYPE_FLOAT_INPUT); + // } + // + // for (const InputChannelId& channelId : InputDeviceXRController::ControllerPosePosition::All) + // { + // createXrAction(channelId, XR_ACTION_TYPE_POSE_INPUT); + // } + // + // for (const InputChannelId& channelId : InputDeviceXRController::ControllerPoseOrientation::All) + // { + // createXrAction(channelId, XR_ACTION_TYPE_POSE_INPUT); // is this correct? + // } + // + // m_xrControllerImpl->RegisterTickCallback([this](){ PollActions(); }); + // } + // + // AZ::RHI::ResultCode Action::InitializeActionSpace(XrSession xrSession) + // { + // XrActionSpaceCreateInfo actionSpaceInfo{}; + // actionSpaceInfo.type = XR_TYPE_ACTION_SPACE_CREATE_INFO; + // actionSpaceInfo.action = GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::LPos); + // actionSpaceInfo.poseInActionSpace.orientation.w = 1.f; + // actionSpaceInfo.subactionPath = m_handSubactionPath[static_cast(XR::Side::Left)]; + // + // XrResult result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast(XR::Side::Left)]); + // WARN_IF_UNSUCCESSFUL(result); + // RETURN_RESULTCODE_IF_UNSUCCESSFUL(ConvertResult(result)); + // + // actionSpaceInfo.action = GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::RPos); + // actionSpaceInfo.subactionPath = m_handSubactionPath[static_cast(XR::Side::Right)]; + // + // result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast(XR::Side::Right)]); + // WARN_IF_UNSUCCESSFUL(result); + // + // return ConvertResult(result); + // } + // + // AZ::RHI::ResultCode Action::InitializeActionSets(XrSession xrSession) const + // { + // XrSessionActionSetsAttachInfo attachInfo{}; + // attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO; + // attachInfo.countActionSets = 1; + // attachInfo.actionSets = &m_actionSet; + // + // const XrResult result = xrAttachSessionActionSets(xrSession, &attachInfo); + // WARN_IF_UNSUCCESSFUL(result); + // + // return ConvertResult(result); + // } + // + // void Action::ShutdownInternal() + // { + // if (m_actionSet != XR_NULL_HANDLE) + // { + // for (const auto hand : { XR::Side::Left, XR::Side::Right }) + // { + // xrDestroySpace(m_handSpace[static_cast(hand)]); + // } + // xrDestroyActionSet(m_actionSet); + // } + // + // // Turn off the tick callback and reset the (non-owning) impl pointer back to null + // m_xrControllerImpl->RegisterTickCallback(nullptr); + // m_xrControllerImpl = nullptr; + // } + // + // XrAction Action::GetAction(const AzFramework::InputChannelId& channelId) const + // { + // // this is a private function and only input channel ids that were used to + // // initialize structures in this class should be passed. + // + // // "at" will assert if the channelId is something unexpected for xr controller + // const auto index = m_xrActionIndices.at(channelId); + // return m_xrActionPaths[index].action; + // } + // + // void Action::PollActions() + // { + // const auto session = static_cast(GetDescriptor().m_session.get()); + // XrSession xrSession = session->GetXrSession(); + // m_handActive = { XR_FALSE, XR_FALSE }; + // + // auto& rawControllerData = m_xrControllerImpl->GetRawState(); + // + // // Might not need to reset if we're constantly refreshing all raw values. + // // In the future we may want to store off a couple ticks of data in a history + // // so that derivatives and edge detection can be computed. + // rawControllerData.Reset(); + // + // // Sync actions + // const XrActiveActionSet activeActionSet{ m_actionSet, XR_NULL_PATH }; + // XrActionsSyncInfo syncInfo{}; + // syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO; + // syncInfo.countActiveActionSets = 1; + // syncInfo.activeActionSets = &activeActionSet; + // + // XrResult result = xrSyncActions(xrSession, &syncInfo); + // if (result != XR_SUCCESS) + // { + // // This will hit when the device gets put down / goes idle. + // // So to avoid spam, just return here. + // return; + // } + // + // using namespace AzFramework; + // using xrc = InputDeviceXRController; + // + // // Updating digital buttons is somewhat unique, because it compacts and combines them all to a u32 with bit masks... + // for (const auto& [channelId, bitMask] : rawControllerData.m_buttonIdsToBitMasks) + // { + // XrActionStateGetInfo getButtonInfo{}; + // getButtonInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + // getButtonInfo.next = nullptr; + // getButtonInfo.action = GetAction(channelId); + // getButtonInfo.subactionPath = XR_NULL_PATH; + // + // XrActionStateBoolean buttonValue{}; + // buttonValue.type = XR_TYPE_ACTION_STATE_BOOLEAN; + // + // result = xrGetActionStateBoolean(xrSession, &getButtonInfo, &buttonValue); + // WARN_IF_UNSUCCESSFUL(result); + // + // rawControllerData.m_digitalButtonStates |= ( + // (buttonValue.isActive == XR_TRUE && buttonValue.currentState == XR_TRUE) + // ? bitMask + // : 0 + // ); + // } + // + // // lambda that obtains a float state from an action... + // auto getActionStateFloat = [&xrSession, this](const InputChannelId& channelId) -> float + // { + // XrActionStateGetInfo getAnalogInfo{}; + // getAnalogInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + // getAnalogInfo.next = nullptr; + // getAnalogInfo.action = GetAction(channelId); + // getAnalogInfo.subactionPath = XR_NULL_PATH; + // + // XrActionStateFloat analogValue{}; + // analogValue.type = XR_TYPE_ACTION_STATE_FLOAT; + // + // const XrResult result = xrGetActionStateFloat(xrSession, &getAnalogInfo, &analogValue); + // WARN_IF_UNSUCCESSFUL(result); + // + // if (analogValue.isActive == XR_TRUE) + // { + // return analogValue.currentState; + // } + // return 0.f; + // }; + // + // // Update Analog values... + // rawControllerData.m_leftTriggerState = getActionStateFloat(xrc::Trigger::LTrigger); + // rawControllerData.m_rightTriggerState = getActionStateFloat(xrc::Trigger::RTrigger); + // rawControllerData.m_leftGripState = getActionStateFloat(xrc::Trigger::LGrip); + // rawControllerData.m_rightGripState = getActionStateFloat(xrc::Trigger::RGrip); + // rawControllerData.m_leftThumbStickXState = getActionStateFloat(xrc::ThumbStickAxis1D::LX); + // rawControllerData.m_leftThumbStickYState = getActionStateFloat(xrc::ThumbStickAxis1D::LY); + // rawControllerData.m_rightThumbStickXState = getActionStateFloat(xrc::ThumbStickAxis1D::RX); + // rawControllerData.m_rightThumbStickYState = getActionStateFloat(xrc::ThumbStickAxis1D::RY); + // + // // Scale the rendered hand by 1.0f (open) to 0.5f (fully squeezed). + // m_handScale[static_cast(XR::Side::Left)] = 1.f - 0.5f * rawControllerData.m_leftGripState; + // m_handScale[static_cast(XR::Side::Right)] = 1.f - 0.5f * rawControllerData.m_rightGripState; + // + // // lambda that outputs vibration amount to a particular side + // auto setHapticVibration = [this, &xrSession](AZ::u32 side, float amount) + // { + // if (amount > 0.f) + // { + // XrHapticVibration hapticVibration{}; + // hapticVibration.type = XR_TYPE_HAPTIC_VIBRATION; + // hapticVibration.amplitude = amount; + // hapticVibration.duration = XR_MIN_HAPTIC_DURATION; + // hapticVibration.frequency = XR_FREQUENCY_UNSPECIFIED; + // + // XrHapticActionInfo hapticActionInfo{}; + // hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO; + // hapticActionInfo.action = m_hapticAction; + // hapticActionInfo.subactionPath = m_handSubactionPath[side]; + // + // [[maybe_unused]] const XrResult result = xrApplyHapticFeedback( + // xrSession, &hapticActionInfo, reinterpret_cast(&hapticVibration)); + // WARN_IF_UNSUCCESSFUL(result); + // } + // }; + // + // setHapticVibration(static_cast(XR::Side::Left), rawControllerData.m_leftMotorVibrationValue); + // setHapticVibration(static_cast(XR::Side::Right), rawControllerData.m_rightMotorVibrationValue); + // // after the vibration values have been used, reset them + // rawControllerData.m_leftMotorVibrationValue = 0.f; + // rawControllerData.m_rightMotorVibrationValue = 0.f; + // + // // Check if the Quit (Home) button was pressed this sync... + // const bool quitPressed = GetButtonState(InputDeviceXRController::Button::Home); + // if (quitPressed && !m_wasQuitPressedLastSync) + // { + // result = xrRequestExitSession(xrSession); + // WARN_IF_UNSUCCESSFUL(result); + // } + // m_wasQuitPressedLastSync = quitPressed; + // } + // + // + // bool Action::UpdateXrSpaceLocations(const OpenXRVk::Device& device, XrTime predictedDisplayTime, AZStd::vector& xrViews) + // { + // const auto thisDevice = static_cast(GetDescriptor().m_device.get()); + // if (thisDevice != &device) + // { + // return false; + // } + // + // auto& rawControllerData = m_xrControllerImpl->GetRawState(); + // const auto session = static_cast(GetDescriptor().m_session.get()); + // XrSession xrSession = session->GetXrSession(); + // XrSpace xrBaseSpaceForVisualization = session->GetXrSpace(session->GetBaseSpaceTypeForVisualization()); + // XrSpace xrBaseSpaceForJoysticks = session->GetXrSpace(session->GetBaseSpaceTypeForControllers()); + // + // // Update poses + // for (const auto hand : { XR::Side::Left, XR::Side::Right }) + // { + // XrActionStateGetInfo getInfo{}; + // getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; + // getInfo.action = GetPoseAction(static_cast(hand)); + // + // XrActionStatePose poseState{}; + // poseState.type = XR_TYPE_ACTION_STATE_POSE; + // + // XrResult result = xrGetActionStatePose(xrSession, &getInfo, &poseState); + // WARN_IF_UNSUCCESSFUL(result); + // m_handActive[static_cast(hand)] = poseState.isActive; + // + // LocateControllerSpace(predictedDisplayTime, xrBaseSpaceForJoysticks, static_cast(hand)); + // } + // + // // Cache 3d location information + // for (AZ::u32 i = 0; i < static_cast(SpaceType::Count); i++) + // { + // const auto spaceType = static_cast(i); + // LocateVisualizedSpace(predictedDisplayTime, session->GetXrSpace(spaceType), + // xrBaseSpaceForVisualization, spaceType); + // } + // + // rawControllerData.m_leftPositionState = AzPositionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Left)].pose); + // rawControllerData.m_rightPositionState = AzPositionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Right)].pose); + // + // rawControllerData.m_leftOrientationState = AzQuaternionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Left)].pose); + // rawControllerData.m_rightOrientationState = AzQuaternionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Right)].pose); + // + // if (LocateEyeViews(predictedDisplayTime, xrViews)) + // { + // //! Time to notify the engine that we have new poses. + // const auto& xrSpaceLocationHeadToBase = m_xrVisualizedSpaceLocations[OpenXRVk::SpaceType::View]; + // const auto baseToHeadTm = AzTransformFromXrPose(xrSpaceLocationHeadToBase.pose); + // const auto headToLeftEyeTm = AzTransformFromXrPose(xrViews[0].pose); + // const auto headToRightEyeTm = AzTransformFromXrPose(xrViews[1].pose); + // + // AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, + // baseToHeadTm, headToLeftEyeTm, headToRightEyeTm); + // + // return true; + // } + // + // return false; + // } + // + // bool Action::LocateEyeViews(XrTime predictedDisplayTime, AZStd::vector& xrViews) + // { + // const auto session = static_cast(GetDescriptor().m_session.get()); + // XrSession xrSession = session->GetXrSession(); + // const auto xrVkInstance = static_cast(GetDescriptor().m_instance.get()); + // + // // Let's get the FOV data, which for most practical purposes it is always the same + // // across all frames. But most importantly we need to get the location of each Eye relative to the View Space pose. + // + // Space* xrSpace = static_cast(session->GetSpace()); + // + // XrViewState viewState{ XR_TYPE_VIEW_STATE }; + // uint32_t viewCapacityInput = aznumeric_cast(xrViews.size()); + // uint32_t viewCountOutput = 0; + // + // XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO }; + // viewLocateInfo.viewConfigurationType = xrVkInstance->GetViewConfigType(); + // viewLocateInfo.displayTime = predictedDisplayTime; + // viewLocateInfo.space = xrSpace->GetXrSpace(OpenXRVk::SpaceType::View); + // + // XrResult result = xrLocateViews(xrSession, &viewLocateInfo, &viewState, viewCapacityInput, &viewCountOutput, xrViews.data()); + // ASSERT_IF_UNSUCCESSFUL(result); + // + // if ((viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) == 0 || + // (viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) == 0) + // { + // //There is no valid tracking poses for the views + // return false; + // } + // + // AZ_Error(LogName, viewCountOutput == viewCapacityInput, "Size mismatch between xrLocateViews %i and xrEnumerateViewConfigurationViews %i", viewCountOutput, viewCapacityInput); + // + // return (viewCountOutput == viewCapacityInput); + // } + // + // + // void Action::LocateControllerSpace(XrTime predictedDisplayTime, XrSpace baseSpace, AZ::u32 handIndex) + // { + // XrSpaceLocation spaceLocation{}; + // spaceLocation.type = XR_TYPE_SPACE_LOCATION; + // if (const XrResult result = xrLocateSpace(m_handSpace[handIndex], baseSpace, predictedDisplayTime, &spaceLocation); + // result == XR_SUCCESS) + // { + // if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && + // (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) + // { + // m_handSpaceLocation[handIndex] = spaceLocation; + // } + // } + // } + // + // void Action::LocateVisualizedSpace(XrTime predictedDisplayTime, XrSpace space, XrSpace baseSpace, OpenXRVk::SpaceType visualizedSpaceType) + // { + // XrSpaceLocation spaceLocation{}; + // spaceLocation.type = XR_TYPE_SPACE_LOCATION; + // if (const XrResult result = xrLocateSpace(space, baseSpace, predictedDisplayTime, &spaceLocation); + // result == XR_SUCCESS) + // { + // if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && + // (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) + // { + // m_xrVisualizedSpaceLocations[static_cast(visualizedSpaceType)] = spaceLocation; + // } + // } + // } + // + // AZ::RHI::ResultCode Action::GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData, bool convertToO3de) const + // { + // if (handIndex < AZStd::size(m_handSpaceLocation)) + // { + // outPoseData.m_orientation = AzQuaternionFromXrPose(m_handSpaceLocation[handIndex].pose, convertToO3de); + // outPoseData.m_position = AzPositionFromXrPose(m_handSpaceLocation[handIndex].pose, convertToO3de); + // return AZ::RHI::ResultCode::Success; + // } + // return AZ::RHI::ResultCode::Fail; + // } + // + // AZ::RHI::ResultCode Action::GetControllerTransform(AZ::u32 handIndex, AZ::Transform& outTransform, bool convertToO3de) const + // { + // if (handIndex < AZStd::size(m_handSpaceLocation)) + // { + // outTransform = AzTransformFromXrPose(m_handSpaceLocation[handIndex].pose, convertToO3de); + // outTransform.SetUniformScale(m_handScale[handIndex]); + // return AZ::RHI::ResultCode::Success; + // } + // return AZ::RHI::ResultCode::Fail; + // } + // + // AZ::RHI::ResultCode Action::GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType, AZ::RPI::PoseData& outPoseData, bool convertToO3de) const + // { + // const auto spaceIndex = static_cast(visualizedSpaceType); + // if (spaceIndex < AZStd::size(m_xrVisualizedSpaceLocations)) + // { + // outPoseData.m_orientation = AzQuaternionFromXrPose(m_xrVisualizedSpaceLocations[spaceIndex].pose, convertToO3de); + // outPoseData.m_position = AzPositionFromXrPose(m_xrVisualizedSpaceLocations[spaceIndex].pose, convertToO3de); + // return AZ::RHI::ResultCode::Success; + // } + // return AZ::RHI::ResultCode::Fail; + // } + // + // AZ::RHI::ResultCode Action::GetVisualizedSpaceTransform(OpenXRVk::SpaceType visualizedSpaceType, AZ::Transform& outTransform, bool convertToO3de) const + // { + // const auto spaceIndex = static_cast(visualizedSpaceType); + // if (spaceIndex < AZStd::size(m_xrVisualizedSpaceLocations)) + // { + // outTransform = AzTransformFromXrPose(m_xrVisualizedSpaceLocations[spaceIndex].pose, convertToO3de); + // return AZ::RHI::ResultCode::Success; + // } + // return AZ::RHI::ResultCode::Fail; + // } + // + // float Action::GetControllerScale(AZ::u32 handIndex) const + // { + // return m_handScale[handIndex]; + // } + // + // XrAction Action::GetSqueezeAction(AZ::u32 handIndex) const + // { + // return (handIndex == static_cast(XR::Side::Left)) + // ? GetAction(AzFramework::InputDeviceXRController::Trigger::LGrip) + // : GetAction(AzFramework::InputDeviceXRController::Trigger::RGrip); + // } + // + // XrAction Action::GetPoseAction(AZ::u32 handIndex) const + // { + // return (handIndex == static_cast(XR::Side::Left)) + // ? GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::LPos) + // : GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::RPos); + // } + // + // XrAction Action::GetVibrationAction() const + // { + // return m_hapticAction; + // } + // + // XrAction Action::GetQuitAction() const + // { + // return GetAction(AzFramework::InputDeviceXRController::Button::Home); + // } + // + // bool Action::GetButtonState(const AzFramework::InputChannelId& channelId) const + // { + // const auto& state = m_xrControllerImpl->GetRawState(); + // return state.GetDigitalButtonState(channelId); + // } + // + // bool Action::GetXButtonState() const + // { + // return GetButtonState(AzFramework::InputDeviceXRController::Button::X); + // } + // + // bool Action::GetYButtonState() const + // { + // return GetButtonState(AzFramework::InputDeviceXRController::Button::Y); + // } + // + // bool Action::GetAButtonState() const + // { + // return GetButtonState(AzFramework::InputDeviceXRController::Button::A); + // } + // + // bool Action::GetBButtonState() const + // { + // return GetButtonState(AzFramework::InputDeviceXRController::Button::B); + // } + // + // float Action::GetXJoyStickState(AZ::u32 handIndex) const + // { + // const auto& state = m_xrControllerImpl->GetRawState(); + // return (handIndex == static_cast(XR::Side::Left)) + // ? state.m_leftThumbStickXState + // : state.m_rightThumbStickXState; + // } + // + // float Action::GetYJoyStickState(AZ::u32 handIndex) const + // { + // const auto& state = m_xrControllerImpl->GetRawState(); + // return (handIndex == static_cast(XR::Side::Left)) + // ? state.m_leftThumbStickYState + // : state.m_rightThumbStickYState; + // } + // + // float Action::GetSqueezeState(AZ::u32 handIndex) const + // { + // const auto& state = m_xrControllerImpl->GetRawState(); + // return (handIndex == static_cast(XR::Side::Left)) + // ? state.m_leftGripState + // : state.m_rightGripState; + // } + // + // float Action::GetTriggerState(AZ::u32 handIndex) const + // { + // const auto& state = m_xrControllerImpl->GetRawState(); + // return (handIndex == static_cast(XR::Side::Left)) + // ? state.m_leftTriggerState + // : state.m_rightTriggerState; + // } + +} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h new file mode 100644 index 000000000..c2ecf8539 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -0,0 +1,117 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +//#include +//#include +//#include +//#include + +#include + +#include + +#include "OpenXRActionsBindingAsset.h" + +namespace OpenXRVk +{ + // Class that will help manage XrActionSet/XrAction. + // All XrActionSet and XrActions will be created from an + // asset created by the developer where all actions of interest + // are instantiated. + class ActionsManager final : + public OpenXRActionsInterface::Registrar + { + public: + AZ_CLASS_ALLOCATOR(ActionsManager, AZ::SystemAllocator); + AZ_RTTI(ActionsManager, "{79E2B285-073B-4042-93ED-B92E6C819CA6}"); + + static constexpr char LogName[] = "OpenXRVkActionsManager"; + + //! Initialize various actions and actionSets according to the + //! "openxr.xractions" action bindings asset. + bool Init(XrInstance xrInstance, XrSession xrSession); + + //! Called by the Session each tick. + bool SyncActions(); + + ///////////////////////////////////////////////// + /// OpenXRActionsInterface overrides + AZStd::vector GetAllActionSets() const override; + AZStd::vector GetActiveActionSets() const override; + AZStd::vector GetInactiveActionSets() const override; + AZ::Outcome ChangeActionSetState(const AZStd::string& actionSetName, bool activate) override; + AZ::Outcome ChangeActionSetsState(const AZStd::vector& actionSetNames, bool activate) override; + + ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const = 0; + + AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) = 0; + AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) = 0; + AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) = 0; + AZ::Outcome GetActionStatePose(ActionHandle actionHandle) = 0; + + AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) = 0; + AZ::Outcome StopHapticVibrationAction(ActionHandle actionHandle) = 0; + /// OpenXRActionsInterface overrides + ///////////////////////////////////////////////// + + private: + + // Asset Cache relative path + static constexpr char DefaultActionsAssetPath[] = "openxr.xractions"; + + + bool InitActionSetInternal(const OpenXRActionSet& actionSet, + AZStd::unordered_set& activeProfiles, + AZStd::vector& activeBindings); + + struct ActionInfo + { + AZStd::string m_name; + XrActionType m_actionType; + // The index in @m_xrActions; + IOpenXRActions::ActionHandle m_actionHandle; + }; + + struct ActionSetInfo + { + AZStd::string m_name; + XrActionSet m_xrActionSet; + // The key is the name of the action. + AZStd::unordered_map m_actions; + }; + + bool InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, + AZStd::unordered_set& activeProfiles, + AZStd::vector& activeBindings); + + + AZ::Outcome ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false); + void RecreateXrActiveActionSets(); + + XrInstance m_xrInstance = XR_NULL_HANDLE; + XrSession m_xrSession = XR_NULL_HANDLE; + + //! Each actionSet in this list is guaranteed to contain at least one valid action. + AZStd::vector m_actionSets; + //! This is a flat list of all XrActions across all actionSets. + //! An IOpenXRActions::ActionHandle is actually an index into this list. + AZStd::vector m_xrActions; + + //! 32 action sets should be enough + static constexpr uint32_t MaxActionSets = 32; + // Each time ChangeActionSetState or ChangeActionSetsState is called + // the following lists are updated: + //! Each bit is an index in @m_actionsSets + AZStd::bitset m_activeActionSets; + //! Here we cache the list of OpenXR native handles for active action sets. + AZStd::vector m_xrActiveActionSets; + + }; +} diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index d960a443d..95bf96fc9 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -6,16 +6,21 @@ * */ -#include +#include +#include + +#include + #include #include #include #include #include -#include -#include +#include + #include -#include + +#include "OpenXRActionsManager.h" namespace OpenXRVk { @@ -46,7 +51,11 @@ namespace OpenXRVk createInfo.systemId = xrVkInstance->GetXRSystemId(); XrResult result = xrCreateSession(m_xrInstance, &createInfo, &m_session); ASSERT_IF_UNSUCCESSFUL(result); - + + m_actionsMgr = AZStd::make_unique(); + bool actionsSuccess = m_actionsMgr->Init(m_xrInstance, m_session); + AZ_Assert(actionsSuccess, "Failed to instantiate the actions manager"); + LogReferenceSpaces(); Input* xrVkInput = GetNativeInput(); xrVkInput->InitializeActionSpace(m_session); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp index de630967f..30db6d76c 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp @@ -44,6 +44,17 @@ namespace OpenXRVk return to_string(result); } + void PrintXrError(const char* windowName, const XrResult error, const char* fmt, ...) + { + va_list argList; + va_start(argList, fmt); + + const auto subMsg = AZStd::string::format_arg(fmt, argList); + AZ_Error(windowName, false, "%s. Got OpenXR Error: %s\n", subMsg.c_str(), GetResultString(error)); + + va_end(argList); + } + XR::RawStringList FilterList(const XR::RawStringList& source, const XR::StringList& filter) { XR::RawStringList filteredList; diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index 2c3f646dd..08d2821e6 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -17,7 +17,8 @@ set(FILES Include/OpenXRVk/OpenXRVkSwapChain.h Include/OpenXRVk/OpenXRVkSystemComponent.h Include/OpenXRVk/OpenXRVkUtils.h - Include/OpenXRVk/OpenXRInteractionProviderBus.h + Include/OpenXRVk/OpenXRInteractionProfileBus.h + Include/OpenXRVk/OpenXRActionsInterface.h Source/InputDeviceXRController.cpp Source/OpenXRVkCommon.h Source/OpenXRVkDevice.cpp @@ -35,4 +36,6 @@ set(FILES Source/XRCameraMovementComponent.h Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h + Source/OpenXRActionsManager.cpp + Source/OpenXRActionsManager.h ) diff --git a/Projects/OpenXRTest/Registry/OpenXR.setreg b/Projects/OpenXRTest/Registry/OpenXR.setreg index 75ff5e665..be72bc2f0 100644 --- a/Projects/OpenXRTest/Registry/OpenXR.setreg +++ b/Projects/OpenXRTest/Registry/OpenXR.setreg @@ -5,7 +5,13 @@ "Enable": true, "android_ViewResolutionScale": 1.0, "FoveatedLevel": 3 + }, + "DxcOverridePath": "C:\\Users\\galibfarrieta\\.o3de\\3rdParty\\packages\\DirectXShaderCompilerDxc-1.6.2112-o3de-rev1-windows\\DirectXShaderCompilerDxc\\bin\\Release\\dxc.exe", + "Shaders": { + "Build": { + "ConfigPath": "@projectroot@/Config" + } } } } -} \ No newline at end of file +} diff --git a/Projects/OpenXRTest/project.json b/Projects/OpenXRTest/project.json index 14b5a91a6..db7e25e39 100644 --- a/Projects/OpenXRTest/project.json +++ b/Projects/OpenXRTest/project.json @@ -22,6 +22,8 @@ "restricted": "OpenXRTest", "gem_names": [ "XR>=1.0.1", - "OpenXRVk>=1.0.1" + "OpenXRVk>=1.0.1", + "EditorPythonBindings", + "QtForPython" ] } From cdad2d4b5e075add88ad077a16e26d4a8dc11127 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 12 Jan 2024 15:43:29 -0600 Subject: [PATCH 04/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Code/Source/OpenXRActionsManager.cpp | 44 +++++++++++++------ .../Code/Source/OpenXRActionsManager.h | 28 ++++++------ Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp | 3 +- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 14 +++--- .../Common/Default/OculusTouch_Default.cpp | 13 +++--- 5 files changed, 62 insertions(+), 40 deletions(-) diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index 17cc725b5..0503e1a51 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -130,21 +130,30 @@ namespace OpenXRVk XrActionSetCreateInfo actionSetCreateInfo{}; actionSetCreateInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO; azstrcpy(actionSetCreateInfo.actionSetName, sizeof(actionSetCreateInfo.actionSetName), actionSet.m_name.c_str()); - azstrcpy(actionSetCreateInfo.localizedActionSetName, sizeof(actionSetCreateInfo.localizedActionSetName), actionSet.m_localizedName.c_str()); + const char* localizedNameCStr = actionSet.m_name.c_str(); + if (!actionSet.m_localizedName.empty()) + { + localizedNameCStr = actionSet.m_localizedName.c_str(); + } + azstrcpy(actionSetCreateInfo.localizedActionSetName, sizeof(actionSetCreateInfo.localizedActionSetName), localizedNameCStr); actionSetCreateInfo.priority = actionSet.m_priority; - m_actionSets.push_back({}); - ActionSetInfo& actionSetInfo = m_actionSets.back(); - XrResult result = xrCreateActionSet(m_xrInstance, &actionSetInfo, &actionSetInfo.m_xrActionSet); - if (IsError(result)) { - PrintXrError(LogName, result, "Failed to instantiate actionSet named [%s].", actionSet.m_name.c_str()); - return false; + ActionSetInfo newActionSetInfo; + newActionSetInfo.m_name = actionSet.m_name; + XrResult result = xrCreateActionSet(m_xrInstance, &actionSetCreateInfo, &newActionSetInfo.m_xrActionSet); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to instantiate actionSet named [%s].", actionSet.m_name.c_str()); + return false; + } + m_actionSets.emplace_back(AZStd::move(newActionSetInfo)); } + ActionSetInfo& newActionSetInfo = m_actionSets.back(); for (const auto& action : actionSet.m_actions) { - if (!InitActionBindingsInternal(actionSetInfo, action, activeProfiles, activeBindings)) + if (!InitActionBindingsInternal(newActionSetInfo, action, activeProfiles, activeBindings)) { AZ_Error(LogName, false, "Failed to created action named [%s] under actionSet named [%s].", action.m_name.c_str(), actionSet.m_name.c_str()); @@ -159,7 +168,8 @@ namespace OpenXRVk AZStd::unordered_set& activeProfiles, AZStd::vector& activeBindings) { - // One OpenXRAction object contains a list of XrActions that need to be created. + // One OpenXRAction object will become one XrAction. + // An OpenXRAction contains a list of OpenXRActionPath that need to be bound. // The action type for each XrAction will be the same and it will be determined by // the action type of the first action in the list. AZ_Assert(!action.m_actionPaths.empty(), "OpenXR Actions list must contain at least one action."); @@ -178,7 +188,12 @@ namespace OpenXRVk actionCreateInfo.type = XR_TYPE_ACTION_CREATE_INFO; actionCreateInfo.actionType = firstActionInfo.m_actionType; azstrcpy(actionCreateInfo.actionName, sizeof(actionCreateInfo.actionName), action.m_name.c_str()); - azstrcpy(actionCreateInfo.localizedActionName, sizeof(actionCreateInfo.localizedActionName), action.m_localizedName.c_str()); + const char* localizedNameCStr = action.m_name.c_str(); + if (!action.m_localizedName.empty()) + { + localizedNameCStr = action.m_localizedName.c_str(); + } + azstrcpy(actionCreateInfo.localizedActionName, sizeof(actionCreateInfo.localizedActionName), localizedNameCStr); actionCreateInfo.countSubactionPaths = 0; // Subactions are not supported. actionCreateInfo.subactionPaths = nullptr; // Subactions are not supported. @@ -352,7 +367,7 @@ namespace OpenXRVk XrActionStateBoolean state { XR_TYPE_ACTION_STATE_BOOLEAN }; XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; getInfo.action = m_xrActions[actionIndex]; - XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state)); + XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); @@ -372,7 +387,7 @@ namespace OpenXRVk XrActionStateFloat state{ XR_TYPE_ACTION_STATE_FLOAT }; XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; getInfo.action = m_xrActions[actionIndex]; - XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state)); + XrResult result = xrGetActionStateFloat(m_xrSession, &getInfo, &state); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); @@ -392,7 +407,7 @@ namespace OpenXRVk XrActionStateVector2f state{ XR_TYPE_ACTION_STATE_VECTOR2F }; XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; getInfo.action = m_xrActions[actionIndex]; - XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state)); + XrResult result = xrGetActionStateVector2f(m_xrSession, &getInfo, &state); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); @@ -409,6 +424,7 @@ namespace OpenXRVk return AZ::Failure("Invalid actionHandle!"); } [[maybe_unused]] const auto actionIndex = actionHandle.GetIndex(); + AZ_Assert(false, "FIXME!"); return AZ::Success(AZ::Transform::CreateIdentity()); } @@ -457,7 +473,7 @@ namespace OpenXRVk } /// OpenXRActionsInterface overrides ///////////////////////////////////////////////// - AZ::Outcome ActionsManager::ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false) + AZ::Outcome ActionsManager::ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets) { // First get the index. size_t foundIdx = 0; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index c2ecf8539..27dd6bbb9 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -49,15 +49,15 @@ namespace OpenXRVk AZ::Outcome ChangeActionSetState(const AZStd::string& actionSetName, bool activate) override; AZ::Outcome ChangeActionSetsState(const AZStd::vector& actionSetNames, bool activate) override; - ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const = 0; + ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const override; - AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) = 0; - AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) = 0; - AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) = 0; - AZ::Outcome GetActionStatePose(ActionHandle actionHandle) = 0; + AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) override; + AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) override; + AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) override; + AZ::Outcome GetActionStatePose(ActionHandle actionHandle) override; - AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) = 0; - AZ::Outcome StopHapticVibrationAction(ActionHandle actionHandle) = 0; + AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) override; + AZ::Outcome StopHapticVibrationAction(ActionHandle actionHandle) override; /// OpenXRActionsInterface overrides ///////////////////////////////////////////////// @@ -74,9 +74,10 @@ namespace OpenXRVk struct ActionInfo { AZStd::string m_name; - XrActionType m_actionType; - // The index in @m_xrActions; - IOpenXRActions::ActionHandle m_actionHandle; + XrActionType m_actionType = XR_ACTION_TYPE_MAX_ENUM; + XrAction m_xrAction = XR_NULL_HANDLE; + // Only valid if m_actionType is XR_ACTION_TYPE_POSE_INPUT + XrSpace m_xrSpace = XR_NULL_HANDLE; }; struct ActionSetInfo @@ -84,7 +85,8 @@ namespace OpenXRVk AZStd::string m_name; XrActionSet m_xrActionSet; // The key is the name of the action. - AZStd::unordered_map m_actions; + // The value is the index in @m_actions; + AZStd::unordered_map m_actions; }; bool InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, @@ -100,9 +102,9 @@ namespace OpenXRVk //! Each actionSet in this list is guaranteed to contain at least one valid action. AZStd::vector m_actionSets; - //! This is a flat list of all XrActions across all actionSets. + //! This is a flat list of all actions across all actionSets. //! An IOpenXRActions::ActionHandle is actually an index into this list. - AZStd::vector m_xrActions; + AZStd::vector m_actions; //! 32 action sets should be enough static constexpr uint32_t MaxActionSets = 32; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp index b0c129247..e33454b2e 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp @@ -53,7 +53,8 @@ namespace OpenXRVk WARN_IF_UNSUCCESSFUL(result); } - // Notify the input system that we have a new predicted display time. + // Now that we have a new predicted display time, the session should be able + // to sync actions and locate spaces. // The new predicted display time will be used to calculate XrPoses for the current frame. session->UpdateXrSpaceLocations(*this, m_frameState.predictedDisplayTime, m_views); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index 95bf96fc9..fe5c89190 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -54,12 +54,13 @@ namespace OpenXRVk m_actionsMgr = AZStd::make_unique(); bool actionsSuccess = m_actionsMgr->Init(m_xrInstance, m_session); - AZ_Assert(actionsSuccess, "Failed to instantiate the actions manager"); + AZ_Error("OpenXRVk::Session", actionsSuccess, "Failed to instantiate the actions manager"); LogReferenceSpaces(); - Input* xrVkInput = GetNativeInput(); - xrVkInput->InitializeActionSpace(m_session); - xrVkInput->InitializeActionSets(m_session); + + //Input* xrVkInput = GetNativeInput(); + //xrVkInput->InitializeActionSpace(m_session); + //xrVkInput->InitializeActionSets(m_session); Space* xrVkSpace = static_cast(GetSpace()); xrVkSpace->CreateVisualizedSpaces(m_session); @@ -432,8 +433,9 @@ namespace OpenXRVk return static_cast(GetInput()); } - void Session::UpdateXrSpaceLocations(const OpenXRVk::Device& device, XrTime predictedDisplayTime, AZStd::vector& xrViews) + void Session::UpdateXrSpaceLocations([[maybe_unused]] const OpenXRVk::Device& device, [[maybe_unused]] XrTime predictedDisplayTime, [[maybe_unused]] AZStd::vector& xrViews) { - GetNativeInput()->UpdateXrSpaceLocations(device, predictedDisplayTime, xrViews); + m_actionsMgr->SyncActions(); + //GetNativeInput()->UpdateXrSpaceLocations(device, predictedDisplayTime, xrViews); } } diff --git a/Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.cpp b/Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.cpp index 6f8aad84e..3e0ce18ae 100644 --- a/Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.cpp +++ b/Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.cpp @@ -150,12 +150,13 @@ namespace OpenXRVk //////////////////////////////////////////////////////////////////////////////////////////////// void InputDeviceOculusTouch::TickInputDevice() { - if (m_tickCallback) - { - m_tickCallback(); - - ProcessRawControllerState(m_rawControllerState); - } + // GALIB: REMOVE ME + // if (m_tickCallback) + // { + // m_tickCallback(); + // + // ProcessRawControllerState(m_rawControllerState); + // } } } // namespace OpenXRVk From c20959fbfc438456f6e9967e6fe697b345a023c1 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sat, 13 Jan 2024 11:48:10 -0600 Subject: [PATCH 05/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Include/OpenXRVk/OpenXRActionsInterface.h | 16 +- .../Code/Include/OpenXRVk/OpenXRVkUtils.h | 2 +- .../Code/Source/OpenXRActionsManager.cpp | 207 ++++++++++++++---- .../Code/Source/OpenXRActionsManager.h | 20 +- Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp | 11 +- 5 files changed, 196 insertions(+), 60 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h index 1d5fa53ce..40eb0adcb 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h @@ -16,6 +16,12 @@ namespace OpenXRVk { + struct PoseWithVelocities + { + AZ::Transform m_pose; + AZ::Vector3 m_linearVelocity; + AZ::Vector3 m_angularVelocity; + }; //! Interface used to query the state of actions and also //! to drive the state of haptic feedback actions. @@ -40,10 +46,12 @@ namespace OpenXRVk virtual ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const = 0; - virtual AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) = 0; - virtual AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) = 0; - virtual AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) = 0; - virtual AZ::Outcome GetActionStatePose(ActionHandle actionHandle) = 0; + virtual AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) const = 0; + virtual AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const = 0; + virtual AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) const = 0; + virtual AZ::Outcome GetActionStatePose(ActionHandle actionHandle) const = 0; + //! Same as above, but also queries location (linear) and orientation (angular) velocities + virtual AZ::Outcome GetActionStatePoseWithVelocities(ActionHandle actionHandle) const = 0; virtual AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) = 0; virtual AZ::Outcome StopHapticVibrationAction(ActionHandle actionHandle) = 0; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h index 21e0a0b67..0a23f2127 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h @@ -68,8 +68,8 @@ namespace OpenXRVk AZStd::vector ParseExtensionString(char* names); AZ::Quaternion AzQuaternionFromXrPose(const XrPosef& pose, bool convertCoordinates = true); + AZ::Vector3 AzVector3FromXrVector3(const XrVector3f& xrVec3, bool convertCoordinates = true); AZ::Vector3 AzPositionFromXrPose(const XrPosef& pose, bool convertCoordinates = true); AZ::Transform AzTransformFromXrPose(const XrPosef& pose, bool convertCoordinates = true); XrPosef XrPoseFromAzTransform(const AZ::Transform& tm, bool convertCoordinates = true); - } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index 0503e1a51..42f042eb7 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -99,7 +99,7 @@ namespace OpenXRVk return true; } - bool ActionsManager::SyncActions() + bool ActionsManager::SyncActions(XrTime predictedDisplayTime, XrSpace baseSpace) { if (m_xrActiveActionSets.empty()) { @@ -107,6 +107,9 @@ namespace OpenXRVk return true; } + m_predictedDisplaytime = predictedDisplayTime; + m_baseSpace = baseSpace; + XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO }; syncInfo.countActiveActionSets = aznumeric_cast(m_xrActiveActionSets.size()); syncInfo.activeActionSets = m_xrActiveActionSets.data(); @@ -164,29 +167,23 @@ namespace OpenXRVk return true; } - bool ActionsManager::InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, - AZStd::unordered_set& activeProfiles, - AZStd::vector& activeBindings) + static XrActionType GetActionTypeFromOpenXRActionPath(const OpenXRActionPath& actionPath) { - // One OpenXRAction object will become one XrAction. - // An OpenXRAction contains a list of OpenXRActionPath that need to be bound. - // The action type for each XrAction will be the same and it will be determined by - // the action type of the first action in the list. - AZ_Assert(!action.m_actionPaths.empty(), "OpenXR Actions list must contain at least one action."); - const auto& firstAction = action.m_actionPaths[0]; - - auto interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(firstAction.m_interactionProfile); + auto interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(actionPath.m_interactionProfile); if (!interactionProviderIface) { - AZ_Error(LogName, false, "Couldn't find interaction data provider with id [%s].", firstAction.m_interactionProfile.c_str()) - return false; + AZ_Error(ActionsManager::LogName, false, "Couldn't find interaction data provider with id [%s].", actionPath.m_interactionProfile.c_str()); + return XR_ACTION_TYPE_MAX_ENUM; } + const auto actionPathInfo = interactionProviderIface->GetActionPathInfo(actionPath.m_userPath, actionPath.m_componentPath); + return actionPathInfo.m_actionType; + } - const auto firstActionInfo = interactionProviderIface->GetActionPathInfo(firstAction.m_userPath, firstAction.m_componentPath); - - XrActionCreateInfo actionCreateInfo{}; - actionCreateInfo.type = XR_TYPE_ACTION_CREATE_INFO; - actionCreateInfo.actionType = firstActionInfo.m_actionType; + XrAction ActionsManager::CreateXrActionAndXrSpace(const ActionSetInfo& actionSetInfo, + const OpenXRAction& action, const XrActionType actionType, XrSpace& newXrActionSpace) const + { + XrActionCreateInfo actionCreateInfo{ XR_TYPE_ACTION_CREATE_INFO }; + actionCreateInfo.actionType = actionType; azstrcpy(actionCreateInfo.actionName, sizeof(actionCreateInfo.actionName), action.m_name.c_str()); const char* localizedNameCStr = action.m_name.c_str(); if (!action.m_localizedName.empty()) @@ -201,15 +198,38 @@ namespace OpenXRVk XrResult result = xrCreateAction(actionSetInfo.m_xrActionSet, &actionCreateInfo, &newXrAction); if (IsError(result)) { - PrintXrError(LogName, result, "Failed to create action named %s.\n", action.m_name.c_str()); - return false; + PrintXrError(ActionsManager::LogName, result, "Failed to create action named %s.\n", action.m_name.c_str()); + return XR_NULL_HANDLE; } - // For each actionPath in the list, create the XrPath and its binding. + // The space will be relevant if the action type is POSE. + newXrActionSpace = XR_NULL_HANDLE; + if (actionType == XR_ACTION_TYPE_POSE_INPUT) + { + XrActionSpaceCreateInfo actionSpaceInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO }; + actionSpaceInfo.action = newXrAction; + actionSpaceInfo.poseInActionSpace.orientation.w = 1.f; // Make it an identity quaterion. + result = xrCreateActionSpace(m_xrSession, &actionSpaceInfo, &newXrActionSpace); + if (IsError(result)) + { + xrDestroyAction(newXrAction); + PrintXrError(ActionsManager::LogName, result, "Failed to create XrSpace for action named %s.\n", action.m_name.c_str()); + return XR_NULL_HANDLE; + } + } + + return newXrAction; + } + + uint32_t ActionsManager::AppendActionBindings(const OpenXRAction& action, + XrAction newXrAction, + AZStd::unordered_set& activeProfiles, + AZStd::vector& activeBindings) const + { uint32_t additionalBindingsCount = 0; for (const auto& actionPath : action.m_actionPaths) { - interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(actionPath.m_interactionProfile); + auto interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(actionPath.m_interactionProfile); if (!interactionProviderIface) { AZ_Error(LogName, false, "Couldn't find interaction data provider with id [%s].", actionPath.m_interactionProfile.c_str()) @@ -225,7 +245,7 @@ namespace OpenXRVk } XrPath xrBindingPath; - result = xrStringToPath(m_xrInstance, pathInfo.m_absolutePath.c_str(), &xrBindingPath); + XrResult result = xrStringToPath(m_xrInstance, pathInfo.m_absolutePath.c_str(), &xrBindingPath); if (IsError(result)) { PrintXrError(LogName, result, "Failed to create XrPath for action with profile [%s], absolute path [%s].\n", @@ -251,22 +271,50 @@ namespace OpenXRVk additionalBindingsCount++; } + return additionalBindingsCount; + } + + bool ActionsManager::InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, + AZStd::unordered_set& activeProfiles, + AZStd::vector& activeBindings) + { + // One OpenXRAction object will become one XrAction. + // An OpenXRAction contains a list of OpenXRActionPath that need to be bound. + // The action type for each XrAction will be the same and it will be determined by + // the action type of the first action in the list. + AZ_Assert(!action.m_actionPaths.empty(), "OpenXR Actions list must contain at least one action."); + XrActionType firstActionType = GetActionTypeFromOpenXRActionPath(action.m_actionPaths[0]); + + XrSpace newXrActionSpace = XR_NULL_HANDLE; // Optional. + XrAction newXrAction = CreateXrActionAndXrSpace(actionSetInfo, action, firstActionType, newXrActionSpace); + if (newXrAction == XR_NULL_HANDLE) + { + return false; + } + + // For each actionPath in the list, create the XrPath and its binding. + const auto additionalBindingsCount = AppendActionBindings(action, newXrAction, activeProfiles, activeBindings); if (additionalBindingsCount < 1) { - // This action has no bindings. Remove it. - AZ_Warning(LogName, false, "The action [] had no bindings!.\n", action.m_name.c_str()); + // This action has no bindings. Don't add it to the active actions list. + AZ_Warning(LogName, false, "The action [%s] had no bindings!.\n", action.m_name.c_str()); + if (newXrActionSpace != XR_NULL_HANDLE) + { + xrDestroySpace(newXrActionSpace); + } xrDestroyAction(newXrAction); return true; } - m_xrActions.push_back(newXrAction); - uint16_t newActionIndex = aznumeric_cast(m_xrActions.size() - 1); - - ActionInfo newActionInfo; + m_actions.push_back({}); + auto& newActionInfo = m_actions.back(); newActionInfo.m_name = action.m_name; - newActionInfo.m_actionType = firstActionInfo.m_actionType; - newActionInfo.m_actionHandle = IOpenXRActions::ActionHandle(newActionIndex); - actionSetInfo.m_actions.emplace(action.m_name, AZStd::move(newActionInfo)); + newActionInfo.m_actionType = firstActionType; + newActionInfo.m_xrAction = newXrAction; + newActionInfo.m_xrSpace = newXrActionSpace; + + uint16_t newActionIndex = aznumeric_cast(m_actions.size() - 1); + actionSetInfo.m_actions.emplace(action.m_name, IOpenXRActions::ActionHandle(newActionIndex)); return true; } @@ -351,12 +399,12 @@ namespace OpenXRVk { return IOpenXRActions::ActionHandle::Null; } - return itor->second.m_actionHandle; + return itor->second; } return IOpenXRActions::ActionHandle::Null; } - AZ::Outcome ActionsManager::GetActionStateBoolean(ActionHandle actionHandle) + AZ::Outcome ActionsManager::GetActionStateBoolean(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) { @@ -366,7 +414,7 @@ namespace OpenXRVk XrActionStateBoolean state { XR_TYPE_ACTION_STATE_BOOLEAN }; XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; - getInfo.action = m_xrActions[actionIndex]; + getInfo.action = m_actions[actionIndex].m_xrAction; XrResult result = xrGetActionStateBoolean(m_xrSession, &getInfo, &state); if (IsError(result)) { @@ -376,7 +424,7 @@ namespace OpenXRVk return AZ::Success(state.currentState); } - AZ::Outcome ActionsManager::GetActionStateFloat(ActionHandle actionHandle) + AZ::Outcome ActionsManager::GetActionStateFloat(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) { @@ -386,7 +434,7 @@ namespace OpenXRVk XrActionStateFloat state{ XR_TYPE_ACTION_STATE_FLOAT }; XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; - getInfo.action = m_xrActions[actionIndex]; + getInfo.action = m_actions[actionIndex].m_xrAction; XrResult result = xrGetActionStateFloat(m_xrSession, &getInfo, &state); if (IsError(result)) { @@ -396,7 +444,7 @@ namespace OpenXRVk return AZ::Success(state.currentState); } - AZ::Outcome ActionsManager::GetActionStateVector2(ActionHandle actionHandle) + AZ::Outcome ActionsManager::GetActionStateVector2(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) { @@ -406,7 +454,7 @@ namespace OpenXRVk XrActionStateVector2f state{ XR_TYPE_ACTION_STATE_VECTOR2F }; XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; - getInfo.action = m_xrActions[actionIndex]; + getInfo.action = m_actions[actionIndex].m_xrAction; XrResult result = xrGetActionStateVector2f(m_xrSession, &getInfo, &state); if (IsError(result)) { @@ -416,16 +464,81 @@ namespace OpenXRVk return AZ::Success(AZ::Vector2(state.currentState.x, state.currentState.y)); } - AZ::Outcome ActionsManager::GetActionStatePose(ActionHandle actionHandle) + AZ::Outcome ActionsManager::GetActionStatePose(ActionHandle actionHandle) const + { + if (!actionHandle.IsValid()) + { + return AZ::Failure("Invalid actionHandle!"); + } + const auto actionIndex = actionHandle.GetIndex(); + + XrSpaceLocation spaceLocation {XR_TYPE_SPACE_LOCATION}; + XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_baseSpace, m_predictedDisplaytime, &spaceLocation); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + + AZ::Vector3 poseLocation = AZ::Vector3::CreateZero(); + if (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) + { + poseLocation = AzPositionFromXrPose(spaceLocation.pose); + } + + AZ::Quaternion poseOrientation = AZ::Quaternion::CreateIdentity(); + if (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) + { + poseOrientation = AzQuaternionFromXrPose(spaceLocation.pose); + } + + AZ::Transform retPoseTransform = AZ::Transform::CreateFromQuaternionAndTranslation(poseOrientation, poseLocation); + return AZ::Success(retPoseTransform); + } + + AZ::Outcome ActionsManager::GetActionStatePoseWithVelocities(ActionHandle actionHandle) const { - //FIXME! if (!actionHandle.IsValid()) { return AZ::Failure("Invalid actionHandle!"); } - [[maybe_unused]] const auto actionIndex = actionHandle.GetIndex(); - AZ_Assert(false, "FIXME!"); - return AZ::Success(AZ::Transform::CreateIdentity()); + const auto actionIndex = actionHandle.GetIndex(); + + XrSpaceVelocity spaceVelocity{ XR_TYPE_SPACE_VELOCITY }; + XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION, &spaceVelocity}; + XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_baseSpace, m_predictedDisplaytime, &spaceLocation); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + + AZ::Vector3 poseLocation = AZ::Vector3::CreateZero(); + AZ::Vector3 linearVelocity = AZ::Vector3::CreateZero(); + if (spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) + { + poseLocation = AzPositionFromXrPose(spaceLocation.pose); + if (spaceVelocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) + { + linearVelocity = AzVector3FromXrVector3(spaceVelocity.linearVelocity); + } + } + + AZ::Quaternion poseOrientation = AZ::Quaternion::CreateIdentity(); + AZ::Vector3 angularVelocity = AZ::Vector3::CreateZero(); + if (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) + { + poseOrientation = AzQuaternionFromXrPose(spaceLocation.pose); + if (spaceVelocity.velocityFlags & XR_SPACE_VELOCITY_ANGULAR_VALID_BIT) + { + angularVelocity = AzVector3FromXrVector3(spaceVelocity.angularVelocity); + } + } + + PoseWithVelocities retPoseWithVelocities { + AZ::Transform::CreateFromQuaternionAndTranslation(poseOrientation, poseLocation), + linearVelocity, + angularVelocity + }; + return AZ::Success(retPoseWithVelocities); } @@ -444,7 +557,7 @@ namespace OpenXRVk vibration.duration = durationNanos; vibration.frequency = frequencyHz; XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO }; - hapticActionInfo.action = m_xrActions[actionIndex]; + hapticActionInfo.action = m_actions[actionIndex].m_xrAction; XrResult result = xrApplyHapticFeedback(m_xrSession, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration); if (IsError(result)) { @@ -463,7 +576,7 @@ namespace OpenXRVk // fire haptics using output action XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO }; - hapticActionInfo.action = m_xrActions[actionIndex]; + hapticActionInfo.action = m_actions[actionIndex].m_xrAction; XrResult result = xrStopHapticFeedback(m_xrSession, &hapticActionInfo); if (IsError(result)) { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index 27dd6bbb9..b39971565 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -39,7 +39,7 @@ namespace OpenXRVk bool Init(XrInstance xrInstance, XrSession xrSession); //! Called by the Session each tick. - bool SyncActions(); + bool SyncActions(XrTime predictedDisplayTime, XrSpace baseSpace); ///////////////////////////////////////////////// /// OpenXRActionsInterface overrides @@ -51,10 +51,11 @@ namespace OpenXRVk ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const override; - AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) override; - AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) override; - AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) override; - AZ::Outcome GetActionStatePose(ActionHandle actionHandle) override; + AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) const override; + AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const override; + AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) const override; + AZ::Outcome GetActionStatePose(ActionHandle actionHandle) const override; + AZ::Outcome GetActionStatePoseWithVelocities(ActionHandle actionHandle) const override; AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) override; AZ::Outcome StopHapticVibrationAction(ActionHandle actionHandle) override; @@ -93,12 +94,21 @@ namespace OpenXRVk AZStd::unordered_set& activeProfiles, AZStd::vector& activeBindings); + XrAction CreateXrActionAndXrSpace(const ActionSetInfo& actionSetInfo, + const OpenXRAction& action, const XrActionType actionType, XrSpace& newXrActionSpace) const; + + uint32_t AppendActionBindings(const OpenXRAction& action, XrAction newXrAction, + AZStd::unordered_set& activeProfiles, + AZStd::vector& activeBindings) const; AZ::Outcome ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false); void RecreateXrActiveActionSets(); XrInstance m_xrInstance = XR_NULL_HANDLE; XrSession m_xrSession = XR_NULL_HANDLE; + // Updated each time SyncActions is called. + XrTime m_predictedDisplaytime; + XrSpace m_baseSpace; //! Each actionSet in this list is guaranteed to contain at least one valid action. AZStd::vector m_actionSets; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp index 30db6d76c..a7bca9385 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp @@ -95,11 +95,16 @@ namespace OpenXRVk return AZ::Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w); } + AZ::Vector3 AzVector3FromXrVector3(const XrVector3f& xrVec3, bool convertCoordinates) + { + return AZ::Vector3(xrVec3.x, + convertCoordinates ? -xrVec3.z : xrVec3.y, + convertCoordinates ? xrVec3.y : xrVec3.z); + } + AZ::Vector3 AzPositionFromXrPose(const XrPosef& pose, bool convertCoordinates) { - return AZ::Vector3(pose.position.x, - convertCoordinates ? -pose.position.z : pose.position.y, - convertCoordinates ? pose.position.y : pose.position.z); + return AzVector3FromXrVector3(pose.position, convertCoordinates); } AZ::Transform AzTransformFromXrPose(const XrPosef& pose, bool convertCoordinates) From 20244e6ef162225a59afa039600153441167f1a3 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 16 Jan 2024 16:01:16 -0600 Subject: [PATCH 06/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVisualizedSpacesInterface.h | 91 ++++++ .../Code/Source/OpenXRActionsManager.h | 2 +- .../Source/OpenXRVisualizedSpacesManager.cpp | 263 ++++++++++++++++++ .../Source/OpenXRVisualizedSpacesManager.h | 85 ++++++ Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 1 + .../Code/openxrvk_private_common_files.cmake | 3 + 6 files changed, 444 insertions(+), 1 deletion(-) create mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h new file mode 100644 index 000000000..d380ba21b --- /dev/null +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include +#include +#include +#include + +#include + +namespace OpenXRVk +{ + //! Interface used to query the state of actions and also + //! to drive the state of haptic feedback actions. + //! The implementation is encouraged to expose each method + //! of this interface as global functions in the behavior context. + class IOpenXRVisualizedSpaces + { + public: + AZ_RTTI(IOpenXRVisualizedSpaces, "{7B163790-BDBE-4C5B-832E-768CF5CDF585}"); + AZ_DISABLE_COPY_MOVE(IOpenXRVisualizedSpaces); + + IOpenXRVisualizedSpaces() = default; + virtual ~IOpenXRVisualizedSpaces() = default; + + // Identifiers for all reference spaces that all OpenXR implementations + // must support. These are the default system spaces. Most OpenXR implementations + // support additional reference spaces, and you can try to use their id to attempt + // to create an space of them at runtime. This explains why this is an ordinary integral + // instead of an enum. + using ReferenceSpaceId = uint32_t; + static constexpr ReferenceSpaceId ReferenceSpaceIdView = 1; + static constexpr ReferenceSpaceId ReferenceSpaceIdLocal = 2; + static constexpr ReferenceSpaceId ReferenceSpaceIdStage = 3; + // With these names you can refer to the default system spaces. + static constexpr char ReferenceSpaceViewName[] = "View"; // Typically represents the user's head centroid. + static constexpr char ReferenceSpaceLocalName[] = "Local"; + static constexpr char ReferenceSpaceStageName[] = "Stage"; + + virtual AZStd::vector GetVisualizedSpaceNames() const = 0; + virtual AZ::Outcome AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) = 0; + virtual AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) = 0; + + // Returns the Pose of @spaceName relative to @baseSpaceName. + virtual AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const = 0; + + // By default the "View" space is the only space that will be automatically + // located on each frame. The "View" space represents the User's head centroid. + // We need a base space to use a reference when locating the user's head, and + // the default base space is the "Local" space. But you can change that with this API. + // Internally the Pose for each Eye View will be calculated relative to the View Space centroid + // and it will be reported via the notification bus: AZ::RPI::XRSpaceNotificationBus + virtual AZ::Outcome SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) = 0; + virtual const AZStd::string& GetBaseSpaceForViewSpacePose() const = 0; + // The following poses are the same as reported by AZ::RPI::XRSpaceNotificationBus + //! Head/View pose relative to BaseSpaceForHeadCentroid. + virtual const AZ::Transform& GetViewSpacePose() const = 0; + + static constexpr uint32_t LeftEyeView = 0; + static constexpr uint32_t RightEyeView = 1; + + // For an AR application running in a Phone (Mono) the count will be 1. + // For an AR/VR application running a typical headset (Stereo) the count will be 2. + // Some headsets like the Varjo support Quad Views and the eye counts will be 4. + virtual uint32_t GetViewCount() const = 0; + + //! Forces updating the cached pose and projection data for all Views. + //! Each frame, all view (aka eye) poses and projections are updated automatically, making this function optional to use. + //! By default the caller simply gets the per-frame cached version, which should be fine for most applications. + //! This API was added because according to OpenXR xrLocateViews spec: + //! "Repeatedly calling xrLocateViews with the same time may not necessarily return the same result. + //! Instead the prediction gets increasingly accurate as the function is called closer to the + //! given time for which a prediction is made" + virtual void ForceViewPosesCacheUpdate() = 0; + + // Pose of a view(aka eye) relative to the View Space (aka Head) Pose. + // For AR applications running on a Phone (aka Mono view configuration) the Eye View Pose + // is centered exactly where the View Space Centroid is located, so calling this function wouldn't + // make much sense because it'll return an Identity transform. + virtual const AZ::Transform& GetViewPose(uint32_t eyeIndex) const = 0; + }; + + using OpenXRVisualizedSpacesInterface = AZ::Interface; + +} // namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index b39971565..d904c3b82 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -30,7 +30,7 @@ namespace OpenXRVk { public: AZ_CLASS_ALLOCATOR(ActionsManager, AZ::SystemAllocator); - AZ_RTTI(ActionsManager, "{79E2B285-073B-4042-93ED-B92E6C819CA6}"); + AZ_RTTI(ActionsManager, "{79E2B285-073B-4042-93ED-B92E6C819CA6}", IOpenXRActions); static constexpr char LogName[] = "OpenXRVkActionsManager"; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp new file mode 100644 index 000000000..cce38a9f7 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp @@ -0,0 +1,263 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include + +#include "OpenXRVisualizedSpacesManager.h" + +namespace OpenXRVk +{ + bool VisualizedSpacesManager::Init(XrInstance xrInstance, XrSession xrSession, XrViewConfigurationType xrViewConfigurationType, uint32_t numEyeViews) + { + m_xrInstance = xrInstance; + m_xrSession = xrSession; + m_xrViewConfigurationType = xrViewConfigurationType; + m_xrViews.resize_no_construct(numEyeViews); + m_eyeViewPoses.resize_no_construct(numEyeViews); + m_viewSpacePose = AZ::Transform::Identity(); + for (uint32_t eyeViewIdx = 0; eyeViewIdx < numEyeViews; eyeViewIdx++) + { + m_eyeViewPoses[eyeViewIdx] = AZ::Transform::Identity(); + } + + const auto identityTm = AZ::Transform::CreateIdentity(); + AZStd::array, 3> ReferenceSpaces = { + { + {IOpenXRVisualizedSpaces::ReferenceSpaceIdView, IOpenXRVisualizedSpaces::ReferenceSpaceViewName }, + {IOpenXRVisualizedSpaces::ReferenceSpaceIdLocal, IOpenXRVisualizedSpaces::ReferenceSpaceLocalName }, + {IOpenXRVisualizedSpaces::ReferenceSpaceIdStage, IOpenXRVisualizedSpaces::ReferenceSpaceStageName } + } + }; + for (const auto& refPair : ReferenceSpaces) + { + if (auto outcome = AddVisualizedSpace(refPair.first, { refPair.second }, identityTm); + !outcome.IsSuccess()) + { + AZ_Error(LogName, false, "Failed to create default reference space [%s].Reason:\n%s\n", + refPair.second, outcome.GetError().c_str()); + return false; + } + } + + // Set the default base space for View Space pose calculation. + auto outcome = SetBaseSpaceForViewSpacePose({ IOpenXRVisualizedSpaces::ReferenceSpaceLocalName }); + AZ_Assert(outcome.IsSuccess(), "Failed to set the base space for View Space pose location"); + + const auto& viewSpace = m_spaces.at(IOpenXRVisualizedSpaces::ReferenceSpaceViewName); + m_viewSpace = &viewSpace; + return true; + } + + bool VisualizedSpacesManager::UpdateViewSpacePoseAndEyeViewPoses(XrTime predictedDisplayTime) + { + m_predictedDisplaytime = predictedDisplayTime; + + XrSpaceLocation xrSpaceLocation{ XR_TYPE_SPACE_LOCATION }; + auto result = xrLocateSpace(m_viewSpace->m_xrSpace, m_baseSpaceForViewSpace->m_xrSpace, m_predictedDisplaytime, &xrSpaceLocation); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to locate View Space [%s] with base space [%s]", + m_viewSpace->m_name.c_str(), m_baseSpaceForViewSpace->m_name.c_str()); + return false; + } + m_viewSpacePose = AzTransformFromXrPose(xrSpaceLocation.pose); + ForceViewPosesCacheUpdate(); + return true; + } + + ///////////////////////////////////////////////// + /// OpenXRVisualizedSpacesInterface overrides + AZStd::vector VisualizedSpacesManager::GetVisualizedSpaceNames() const + { + AZStd::vector retList; + for (auto const& pair : m_spaces) { + retList.push_back(pair.first); + } + return retList; + } + + AZ::Outcome VisualizedSpacesManager::AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, + const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) + { + if (m_spaces.contains(spaceName)) + { + return AZ::Failure( + AZStd::string::format("A space named [%s] already exists", spaceName.c_str()) + ); + } + XrSpace newXrSpace = CreateXrSpace(XrReferenceSpaceType(referenceSpaceType), poseInReferenceSpace); + if (XR_NULL_HANDLE == newXrSpace) + { + return AZ::Failure( + AZStd::string::format("Failed to create new XrSpace for space named [%s] " + "with reference space type [%u]", spaceName.c_str(), referenceSpaceType) + ); + } + + return AZ::Success(true); + } + + AZ::Outcome VisualizedSpacesManager::RemoveVisualizedSpace(const AZStd::string& spaceName) + { + static const AZStd::unordered_set defaultSystemSpaces { + {IOpenXRVisualizedSpaces::ReferenceSpaceViewName }, + {IOpenXRVisualizedSpaces::ReferenceSpaceLocalName}, + {IOpenXRVisualizedSpaces::ReferenceSpaceStageName} + }; + if (defaultSystemSpaces.contains(spaceName)) + { + return AZ::Failure( + AZStd::string::format("Can not delete space [%s] because it is a system space.", spaceName.c_str()) + ); + } + + // If the space we are about to remove is currently the base space for View Space pose location, then we will fail too. + if (m_baseSpaceForViewSpace && + (m_baseSpaceForViewSpace->m_name == spaceName)) + { + return AZ::Failure(AZStd::string::format("Can not remove space [%s] because it is the base space to locate the [%s] space pose.", + spaceName.c_str(), IOpenXRVisualizedSpaces::ReferenceSpaceViewName)); + } + + auto itor = m_spaces.find(spaceName); + if (itor == m_spaces.end()) + { + AZ_Warning(LogName, false, "A space named [%s] doesn't exist.", spaceName.c_str()); + return AZ::Success(true); + } + + xrDestroySpace(itor->second.m_xrSpace); + m_spaces.erase(itor); + return AZ::Success(true); + } + + + AZ::Outcome VisualizedSpacesManager::GetVisualizedSpacePose(const AZStd::string& spaceName, + const AZStd::string& baseSpaceName) const + { + const auto spaceItor = m_spaces.find(spaceName); + if (spaceItor == m_spaces.end()) + { + return AZ::Failure(AZStd::string::format("Space named [%s] doesn't exist.", + spaceName.c_str())); + } + + const auto baseSpaceItor = m_spaces.find(baseSpaceName); + if (baseSpaceItor == m_spaces.end()) + { + return AZ::Failure(AZStd::string::format("Base space named [%s] doesn't exist.", + baseSpaceName.c_str())); + } + + + XrSpaceLocation xrSpaceLocation{ XR_TYPE_SPACE_LOCATION }; + auto result = xrLocateSpace(spaceItor->second.m_xrSpace, baseSpaceItor->second.m_xrSpace, m_predictedDisplaytime, &xrSpaceLocation); + if (IsError(result)) + { + return AZ::Failure( + AZStd::string::format("Failed to locate visualized space [%s] with base space [%s]. Got Error:[%s].", + spaceName.c_str(), baseSpaceName.c_str(), GetResultString(result)) + ); + } + return AZ::Success(AzTransformFromXrPose(xrSpaceLocation.pose)); + } + + AZ::Outcome VisualizedSpacesManager::SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) + { + const auto baseSpaceItor = m_spaces.find(spaceName); + if (baseSpaceItor == m_spaces.end()) + { + return AZ::Failure( + AZStd::string::format("Can not set new base space named [%s] because it doesn't exist.", + spaceName.c_str()) + ); + } + + m_baseSpaceForViewSpace = &(baseSpaceItor->second); + return AZ::Success(true); + } + + const AZStd::string& VisualizedSpacesManager::GetBaseSpaceForViewSpacePose() const + { + AZ_Assert(m_baseSpaceForViewSpace != nullptr, "A base space is always expected to exist!"); + return m_baseSpaceForViewSpace->m_name; + } + + const AZ::Transform& VisualizedSpacesManager::GetViewSpacePose() const + { + return m_viewSpacePose; + } + + uint32_t VisualizedSpacesManager::GetViewCount() const + { + return aznumeric_cast(m_eyeViewPoses.size()); + } + + void VisualizedSpacesManager::ForceViewPosesCacheUpdate() + { + XrViewState viewState{ XR_TYPE_VIEW_STATE }; + uint32_t viewCapacityInput = aznumeric_cast(m_xrViews.size()); + uint32_t viewCountOutput = 0; + + XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO }; + viewLocateInfo.viewConfigurationType = m_xrViewConfigurationType; + viewLocateInfo.displayTime = m_predictedDisplaytime; + viewLocateInfo.space = m_viewSpace->m_xrSpace; + + XrResult result = xrLocateViews(m_xrSession, &viewLocateInfo, &viewState, viewCapacityInput, &viewCountOutput, m_xrViews.data()); + if (IsError(result) || (viewCapacityInput != viewCountOutput)) + { + PrintXrError(LogName, result, "xrLocateViews failed with viewCapacityInput=%u, viewCountOutput=%u", + viewCapacityInput, viewCountOutput); + return; + } + + AZ_Warning(LogName, (viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) != 0, "View poses position data won't be valid"); + AZ_Warning(LogName, (viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) != 0, "View poses orientation data won't be valid"); + + for (size_t viewIdx = 0; viewIdx < m_xrViews.size(); viewIdx++) + { + m_eyeViewPoses[viewIdx] = AzTransformFromXrPose(m_xrViews[viewIdx].pose); + } + } + + const AZ::Transform& VisualizedSpacesManager::GetViewPose(uint32_t eyeIndex) const + { + if (eyeIndex >= m_eyeViewPoses.size()) + { + // Debug and Profile builds shoud crash here. + AZ_Assert(false, "Can't get View Pose because [%u] is out of bounds", eyeIndex); + // In Release build we'd see this message. + AZ_Error(LogName, false, "Can't get View Pose because [%u] is out of bounds", eyeIndex); + return AZ::Transform::Identity(); + } + + return m_eyeViewPoses[eyeIndex]; + } + /// OpenXRVisualizedSpacesInterface overrides + ///////////////////////////////////////////////// + + XrSpace VisualizedSpacesManager::CreateXrSpace(XrReferenceSpaceType referenceSpaceType, const AZ::Transform& relativePose) + { + XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO }; + referenceSpaceCreateInfo.poseInReferenceSpace = XrPoseFromAzTransform(relativePose); + referenceSpaceCreateInfo.referenceSpaceType = referenceSpaceType; + XrSpace newXrSpace = XR_NULL_HANDLE; + XrResult result = xrCreateReferenceSpace(m_xrSession, &referenceSpaceCreateInfo, &newXrSpace); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to create XrSpace using referenceSpaceType=%u.\n", referenceSpaceType); + return XR_NULL_HANDLE; + } + return newXrSpace; + } + +} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h new file mode 100644 index 000000000..78d1f7567 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include + +namespace OpenXRVk +{ + class VisualizedSpacesManager final : + public OpenXRVisualizedSpacesInterface::Registrar + { + public: + AZ_CLASS_ALLOCATOR(VisualizedSpacesManager, AZ::SystemAllocator); + AZ_RTTI(VisualizedSpacesManager, "{4BC4D0C0-02D4-4676-8352-8BC51306AF02}", IOpenXRVisualizedSpaces) + + static constexpr char LogName[] = "OpenXRVkVisualizedSpacesManager"; + + //! Initialize various actions and actionSets according to the + //! "openxr.xractions" action bindings asset. + bool Init(XrInstance xrInstance, XrSession xrSession, XrViewConfigurationType xrViewConfigurationType, uint32_t numEyeViews); + + //! Called by the Session each tick. + bool UpdateViewSpacePoseAndEyeViewPoses(XrTime predictedDisplayTime); + + ///////////////////////////////////////////////// + /// OpenXRVisualizedSpacesInterface overrides + AZStd::vector GetVisualizedSpaceNames() const override; + AZ::Outcome AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, + const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) override; + AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) override; + + + AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, + const AZStd::string& baseSpaceName) const override; + + AZ::Outcome SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) override; + const AZStd::string& GetBaseSpaceForViewSpacePose() const override; + const AZ::Transform& GetViewSpacePose() const override; + + uint32_t GetViewCount() const override; + void ForceViewPosesCacheUpdate() override; + const AZ::Transform& GetViewPose(uint32_t eyeIndex) const override; + /// OpenXRVisualizedSpacesInterface overrides + ///////////////////////////////////////////////// + + private: + + XrInstance m_xrInstance = XR_NULL_HANDLE; + XrSession m_xrSession = XR_NULL_HANDLE; + XrViewConfigurationType m_xrViewConfigurationType; + + // Updated each time the Session calls UpdateViewSpacePoseAndEyeViewPoses(). + XrTime m_predictedDisplaytime; + + struct VisualizedSpace + { + AZStd::string m_name; + // Runtime data + XrSpace m_xrSpace; + }; + + // At the bare minimum this map will always contain three spaces that can not be removed: + // "View", "Local" and "Stage". + AZStd::unordered_map m_spaces; + + // We cache here the base space what will be used to "locate" the View Space pose. + const VisualizedSpace* m_baseSpaceForViewSpace; + const VisualizedSpace* m_viewSpace; // Cached for convenience. This pointer is set once during initialization and never changes. + + AZ::Transform m_viewSpacePose; + //! The following poses are always relative to @m_viewSpacePose. + AZStd::vector m_eyeViewPoses; + AZStd::vector m_xrViews; + + XrSpace CreateXrSpace(XrReferenceSpaceType referenceSpaceType, const AZ::Transform& relativePose); + }; +} diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index fe5c89190..ba4a34182 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -161,6 +161,7 @@ namespace OpenXRVk void Session::ResetSpaces() { + // FIXME! What about action spaces?? Space* xrVkSpace = static_cast(GetSpace()); xrVkSpace->ShutdownInternal(); xrVkSpace->CreateVisualizedSpaces(m_session); diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index 08d2821e6..068271c83 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -19,6 +19,7 @@ set(FILES Include/OpenXRVk/OpenXRVkUtils.h Include/OpenXRVk/OpenXRInteractionProfileBus.h Include/OpenXRVk/OpenXRActionsInterface.h + Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h Source/InputDeviceXRController.cpp Source/OpenXRVkCommon.h Source/OpenXRVkDevice.cpp @@ -38,4 +39,6 @@ set(FILES Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h Source/OpenXRActionsManager.cpp Source/OpenXRActionsManager.h + Source/OpenXRVisualizedSpacesManager.cpp + Source/OpenXRVisualizedSpacesManager.h ) From c94f992d33b4fd666377ca522ad4fe6ca81ba198 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:38:43 -0600 Subject: [PATCH 07/33] saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Include/OpenXRVk/OpenXRActionsInterface.h | 11 + .../OpenXRVisualizedSpacesInterface.h | 11 +- .../Code/Include/OpenXRVk/OpenXRVkSession.h | 5 +- .../Code/Source/OpenXRActionsManager.cpp | 562 ++---------------- .../Code/Source/OpenXRActionsManager.h | 8 +- .../Source/OpenXRVisualizedSpacesManager.cpp | 15 + .../Source/OpenXRVisualizedSpacesManager.h | 5 + Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 49 +- 8 files changed, 113 insertions(+), 553 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h index 40eb0adcb..2482a0a80 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h @@ -27,6 +27,7 @@ namespace OpenXRVk //! to drive the state of haptic feedback actions. //! The implementation is encouraged to expose each method //! of this interface as global functions in the behavior context. + //! REMARK: This class must be instantiated AFTER OpenXRVisualizedSpacesInterface. class IOpenXRActions { public: @@ -49,7 +50,17 @@ namespace OpenXRVk virtual AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) const = 0; virtual AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const = 0; virtual AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) const = 0; + + //! Pose actions are also known as Action Spaces, and their Pose/Transform is always calculated + //! using a reference/base visualized space. This is a stateful API that can be called at anytime. + //! See OpenXRVisualizedSpacesInterface to get details about Visualized Spaces. + //! By default the base VisualizedSpace is the "View" space, which represents thhe user's head centroid. + virtual AZ::Outcome SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) = 0; + virtual const AZStd::string& GetBaseVisualizedSpaceForPoseActions() const = 0; + + //! The returned transform is relative to the base Visualized Space. virtual AZ::Outcome GetActionStatePose(ActionHandle actionHandle) const = 0; + //! Same as above, but also queries location (linear) and orientation (angular) velocities virtual AZ::Outcome GetActionStatePoseWithVelocities(ActionHandle actionHandle) const = 0; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h index d380ba21b..89aa95ffc 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h @@ -16,10 +16,7 @@ namespace OpenXRVk { - //! Interface used to query the state of actions and also - //! to drive the state of haptic feedback actions. - //! The implementation is encouraged to expose each method - //! of this interface as global functions in the behavior context. + //! REMARK: This class must be instantiated BEFORE OpenXRActionsInterface. class IOpenXRVisualizedSpaces { public: @@ -47,6 +44,10 @@ namespace OpenXRVk virtual AZ::Outcome AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) = 0; virtual AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) = 0; + //! REMARK: Maybe it's a good idea to move this into a private interface for the OpenXRVk Gem, + //! and privately use the native handle (XrSpace) + virtual const void * GetVisualizedSpaceNativeHandle(const AZStd::string& spaceName) const = 0; + // Returns the Pose of @spaceName relative to @baseSpaceName. virtual AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const = 0; @@ -84,6 +85,8 @@ namespace OpenXRVk // is centered exactly where the View Space Centroid is located, so calling this function wouldn't // make much sense because it'll return an Identity transform. virtual const AZ::Transform& GetViewPose(uint32_t eyeIndex) const = 0; + + virtual const AZ::RPI::FovData& GetViewFovData(uint32_t eyeIndex) const = 0; }; using OpenXRVisualizedSpacesInterface = AZ::Interface; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h index ca8da5afc..d0efd5967 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h @@ -16,6 +16,7 @@ namespace OpenXRVk { class ActionsManager; + class VisualizedSpacesManager; // Class that will help manage XrSession class Session final @@ -86,9 +87,6 @@ namespace OpenXRVk void ShutdownInternal() override; void LogActionSourceName(XrAction action, const AZStd::string_view actionName) const; Input* GetNativeInput() const; - // Spaces are reset every time the proximity sensor turns off, or the user wears the headset - // when the proximity sensor is ON. - void ResetSpaces(); XrSession m_session = XR_NULL_HANDLE; XrSessionState m_sessionState = XR_SESSION_STATE_UNKNOWN; @@ -97,6 +95,7 @@ namespace OpenXRVk XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR }; AZStd::unique_ptr m_actionsMgr; + AZStd::unique_ptr m_visualizedSpacesMgr; // Application defined base space that will used to calculate // the relative pose of all other spaces. diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index 42f042eb7..3686eb7fc 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -8,34 +8,32 @@ #include -//#include #include -//#include -//#include -//#include -//#include -//#include #include "OpenXRActionsBindingAsset.h" #include #include +#include #include "OpenXRActionsManager.h" namespace OpenXRVk { - // XR::Ptr Action::Create() - // { - // const auto newInput = aznew Action; - // //newInput->m_xrController.SetImplementation(&AzFramework::InputDeviceXRController::Implementation::Create); - // //newInput->m_xrControllerImpl = newInput->m_xrController.GetImplementation(); - // return newInput; - // } - bool ActionsManager::Init(XrInstance xrInstance, XrSession xrSession) { m_xrInstance = xrInstance; m_xrSession = xrSession; + auto outcome = SetBaseVisualizedSpaceForPoseActions(IOpenXRVisualizedSpaces::ReferenceSpaceViewName); + if (!outcome.IsSuccess()) + { + const auto outcomeMsg = outcome.TakeError(); + auto errorMsg = AZStd::string::format("Failed to set [%s] as the default base visualized space. Reason:\n%s.", + IOpenXRVisualizedSpaces::ReferenceSpaceViewName, outcomeMsg.c_str()); + AZ_Assert(false, "%s", errorMsg.c_str()); + AZ_Error(LogName, false, "%s", errorMsg.c_str()); + return false; + } + // OpenXR only allows to define ActionSets during session creation. // From the point of view of O3DE, the developer defines action sets // in an asset of type OpenXRActionBindingsAsset. @@ -464,6 +462,36 @@ namespace OpenXRVk return AZ::Success(AZ::Vector2(state.currentState.x, state.currentState.y)); } + AZ::Outcome ActionsManager::SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) + { + if (visualizedSpaceName == m_baseVisualizedSpaceName) + { + return AZ::Success(true); + } + + auto visualizedSpacesIface = OpenXRVisualizedSpacesInterface::Get(); + if (!visualizedSpacesIface) + { + AZ_Assert(false, "The OpenXRVisualizedSpacesInterface doesn't exist!"); + return AZ::Failure("The OpenXRVisualizedSpacesInterface doesn't exist!"); + } + + void* opaqueXrSpace = visualizedSpacesIface->GetVisualizedSpaceNativeHandle(visualizedSpaceName); + if (!opaqueXrSpace) + { + return AZ::Failure("Visualized space with name [%s] doesn't exist. Will keep the current base space named [%s]", + visualizedSpaceName.c_str(), m_baseVisualizedSpaceName.c_str()); + } + m_baseVisualizedSpaceName = visualizedSpaceName; + m_xrBaseVisualizedSpace = reinterpret_cast(opaqueXrSpace); + return AZ::Success(true); + } + + const AZStd::string& ActionsManager::GetBaseVisualizedSpaceForPoseActions() const + { + return m_baseVisualizedSpaceName; + } + AZ::Outcome ActionsManager::GetActionStatePose(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) @@ -473,7 +501,7 @@ namespace OpenXRVk const auto actionIndex = actionHandle.GetIndex(); XrSpaceLocation spaceLocation {XR_TYPE_SPACE_LOCATION}; - XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_baseSpace, m_predictedDisplaytime, &spaceLocation); + XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseVisualizedSpace, m_predictedDisplaytime, &spaceLocation); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); @@ -629,508 +657,4 @@ namespace OpenXRVk } } - // - // - // void Action::CreateAllActions(const XrInstance& xrInstance) - // { - // // Get the XrPath for the left and right hands - we will use them as subaction paths. - // const AZStd::string leftHandPath{ m_xrControllerImpl->GetLeftHandSubPath() }; - // const AZStd::string rightHandPath{ m_xrControllerImpl->GetRightHandSubPath() }; - // - // XrResult result = xrStringToPath(xrInstance, leftHandPath.data(), &m_handSubactionPath[static_cast(XR::Side::Left)]); - // WARN_IF_UNSUCCESSFUL(result); - // result = xrStringToPath(xrInstance, rightHandPath.data(), &m_handSubactionPath[static_cast(XR::Side::Right)]); - // WARN_IF_UNSUCCESSFUL(result); - // - // // Lambda to create an action and path, store them in m_xrActionPaths - // using namespace AzFramework; - // auto createXrAction = [this, &xrInstance](const InputChannelId& channelId, const XrActionType actionType) - // { - // m_xrActionIndices[channelId] = m_xrActionPaths.size(); - // m_xrActionPaths.push_back({}); - // - // CreateAction(m_xrActionPaths.back().action, actionType, channelId.GetName(), channelId.GetName(), - // aznumeric_cast(AZStd::size(m_handSubactionPath)), m_handSubactionPath.data()); - // - // const AZStd::string xrPathStr{ m_xrControllerImpl->GetInputChannelPath(channelId) }; - // [[maybe_unused]] const XrResult pathResult = xrStringToPath(xrInstance, xrPathStr.data(), &m_xrActionPaths.back().binding); - // WARN_IF_UNSUCCESSFUL(pathResult); - // }; - // - // for (const InputChannelId& channelId : InputDeviceXRController::Button::All) - // { - // createXrAction(channelId, XR_ACTION_TYPE_BOOLEAN_INPUT); - // } - // - // for (const InputChannelId& channelId : InputDeviceXRController::Trigger::All) - // { - // createXrAction(channelId, XR_ACTION_TYPE_FLOAT_INPUT); - // } - // - // for (const InputChannelId& channelId : InputDeviceXRController::ThumbStickAxis1D::All) - // { - // createXrAction(channelId, XR_ACTION_TYPE_FLOAT_INPUT); - // } - // - // for (const InputChannelId& channelId : InputDeviceXRController::ControllerPosePosition::All) - // { - // createXrAction(channelId, XR_ACTION_TYPE_POSE_INPUT); - // } - // - // for (const InputChannelId& channelId : InputDeviceXRController::ControllerPoseOrientation::All) - // { - // createXrAction(channelId, XR_ACTION_TYPE_POSE_INPUT); // is this correct? - // } - // - // m_xrControllerImpl->RegisterTickCallback([this](){ PollActions(); }); - // } - // - // AZ::RHI::ResultCode Action::InitializeActionSpace(XrSession xrSession) - // { - // XrActionSpaceCreateInfo actionSpaceInfo{}; - // actionSpaceInfo.type = XR_TYPE_ACTION_SPACE_CREATE_INFO; - // actionSpaceInfo.action = GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::LPos); - // actionSpaceInfo.poseInActionSpace.orientation.w = 1.f; - // actionSpaceInfo.subactionPath = m_handSubactionPath[static_cast(XR::Side::Left)]; - // - // XrResult result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast(XR::Side::Left)]); - // WARN_IF_UNSUCCESSFUL(result); - // RETURN_RESULTCODE_IF_UNSUCCESSFUL(ConvertResult(result)); - // - // actionSpaceInfo.action = GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::RPos); - // actionSpaceInfo.subactionPath = m_handSubactionPath[static_cast(XR::Side::Right)]; - // - // result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast(XR::Side::Right)]); - // WARN_IF_UNSUCCESSFUL(result); - // - // return ConvertResult(result); - // } - // - // AZ::RHI::ResultCode Action::InitializeActionSets(XrSession xrSession) const - // { - // XrSessionActionSetsAttachInfo attachInfo{}; - // attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO; - // attachInfo.countActionSets = 1; - // attachInfo.actionSets = &m_actionSet; - // - // const XrResult result = xrAttachSessionActionSets(xrSession, &attachInfo); - // WARN_IF_UNSUCCESSFUL(result); - // - // return ConvertResult(result); - // } - // - // void Action::ShutdownInternal() - // { - // if (m_actionSet != XR_NULL_HANDLE) - // { - // for (const auto hand : { XR::Side::Left, XR::Side::Right }) - // { - // xrDestroySpace(m_handSpace[static_cast(hand)]); - // } - // xrDestroyActionSet(m_actionSet); - // } - // - // // Turn off the tick callback and reset the (non-owning) impl pointer back to null - // m_xrControllerImpl->RegisterTickCallback(nullptr); - // m_xrControllerImpl = nullptr; - // } - // - // XrAction Action::GetAction(const AzFramework::InputChannelId& channelId) const - // { - // // this is a private function and only input channel ids that were used to - // // initialize structures in this class should be passed. - // - // // "at" will assert if the channelId is something unexpected for xr controller - // const auto index = m_xrActionIndices.at(channelId); - // return m_xrActionPaths[index].action; - // } - // - // void Action::PollActions() - // { - // const auto session = static_cast(GetDescriptor().m_session.get()); - // XrSession xrSession = session->GetXrSession(); - // m_handActive = { XR_FALSE, XR_FALSE }; - // - // auto& rawControllerData = m_xrControllerImpl->GetRawState(); - // - // // Might not need to reset if we're constantly refreshing all raw values. - // // In the future we may want to store off a couple ticks of data in a history - // // so that derivatives and edge detection can be computed. - // rawControllerData.Reset(); - // - // // Sync actions - // const XrActiveActionSet activeActionSet{ m_actionSet, XR_NULL_PATH }; - // XrActionsSyncInfo syncInfo{}; - // syncInfo.type = XR_TYPE_ACTIONS_SYNC_INFO; - // syncInfo.countActiveActionSets = 1; - // syncInfo.activeActionSets = &activeActionSet; - // - // XrResult result = xrSyncActions(xrSession, &syncInfo); - // if (result != XR_SUCCESS) - // { - // // This will hit when the device gets put down / goes idle. - // // So to avoid spam, just return here. - // return; - // } - // - // using namespace AzFramework; - // using xrc = InputDeviceXRController; - // - // // Updating digital buttons is somewhat unique, because it compacts and combines them all to a u32 with bit masks... - // for (const auto& [channelId, bitMask] : rawControllerData.m_buttonIdsToBitMasks) - // { - // XrActionStateGetInfo getButtonInfo{}; - // getButtonInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; - // getButtonInfo.next = nullptr; - // getButtonInfo.action = GetAction(channelId); - // getButtonInfo.subactionPath = XR_NULL_PATH; - // - // XrActionStateBoolean buttonValue{}; - // buttonValue.type = XR_TYPE_ACTION_STATE_BOOLEAN; - // - // result = xrGetActionStateBoolean(xrSession, &getButtonInfo, &buttonValue); - // WARN_IF_UNSUCCESSFUL(result); - // - // rawControllerData.m_digitalButtonStates |= ( - // (buttonValue.isActive == XR_TRUE && buttonValue.currentState == XR_TRUE) - // ? bitMask - // : 0 - // ); - // } - // - // // lambda that obtains a float state from an action... - // auto getActionStateFloat = [&xrSession, this](const InputChannelId& channelId) -> float - // { - // XrActionStateGetInfo getAnalogInfo{}; - // getAnalogInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; - // getAnalogInfo.next = nullptr; - // getAnalogInfo.action = GetAction(channelId); - // getAnalogInfo.subactionPath = XR_NULL_PATH; - // - // XrActionStateFloat analogValue{}; - // analogValue.type = XR_TYPE_ACTION_STATE_FLOAT; - // - // const XrResult result = xrGetActionStateFloat(xrSession, &getAnalogInfo, &analogValue); - // WARN_IF_UNSUCCESSFUL(result); - // - // if (analogValue.isActive == XR_TRUE) - // { - // return analogValue.currentState; - // } - // return 0.f; - // }; - // - // // Update Analog values... - // rawControllerData.m_leftTriggerState = getActionStateFloat(xrc::Trigger::LTrigger); - // rawControllerData.m_rightTriggerState = getActionStateFloat(xrc::Trigger::RTrigger); - // rawControllerData.m_leftGripState = getActionStateFloat(xrc::Trigger::LGrip); - // rawControllerData.m_rightGripState = getActionStateFloat(xrc::Trigger::RGrip); - // rawControllerData.m_leftThumbStickXState = getActionStateFloat(xrc::ThumbStickAxis1D::LX); - // rawControllerData.m_leftThumbStickYState = getActionStateFloat(xrc::ThumbStickAxis1D::LY); - // rawControllerData.m_rightThumbStickXState = getActionStateFloat(xrc::ThumbStickAxis1D::RX); - // rawControllerData.m_rightThumbStickYState = getActionStateFloat(xrc::ThumbStickAxis1D::RY); - // - // // Scale the rendered hand by 1.0f (open) to 0.5f (fully squeezed). - // m_handScale[static_cast(XR::Side::Left)] = 1.f - 0.5f * rawControllerData.m_leftGripState; - // m_handScale[static_cast(XR::Side::Right)] = 1.f - 0.5f * rawControllerData.m_rightGripState; - // - // // lambda that outputs vibration amount to a particular side - // auto setHapticVibration = [this, &xrSession](AZ::u32 side, float amount) - // { - // if (amount > 0.f) - // { - // XrHapticVibration hapticVibration{}; - // hapticVibration.type = XR_TYPE_HAPTIC_VIBRATION; - // hapticVibration.amplitude = amount; - // hapticVibration.duration = XR_MIN_HAPTIC_DURATION; - // hapticVibration.frequency = XR_FREQUENCY_UNSPECIFIED; - // - // XrHapticActionInfo hapticActionInfo{}; - // hapticActionInfo.type = XR_TYPE_HAPTIC_ACTION_INFO; - // hapticActionInfo.action = m_hapticAction; - // hapticActionInfo.subactionPath = m_handSubactionPath[side]; - // - // [[maybe_unused]] const XrResult result = xrApplyHapticFeedback( - // xrSession, &hapticActionInfo, reinterpret_cast(&hapticVibration)); - // WARN_IF_UNSUCCESSFUL(result); - // } - // }; - // - // setHapticVibration(static_cast(XR::Side::Left), rawControllerData.m_leftMotorVibrationValue); - // setHapticVibration(static_cast(XR::Side::Right), rawControllerData.m_rightMotorVibrationValue); - // // after the vibration values have been used, reset them - // rawControllerData.m_leftMotorVibrationValue = 0.f; - // rawControllerData.m_rightMotorVibrationValue = 0.f; - // - // // Check if the Quit (Home) button was pressed this sync... - // const bool quitPressed = GetButtonState(InputDeviceXRController::Button::Home); - // if (quitPressed && !m_wasQuitPressedLastSync) - // { - // result = xrRequestExitSession(xrSession); - // WARN_IF_UNSUCCESSFUL(result); - // } - // m_wasQuitPressedLastSync = quitPressed; - // } - // - // - // bool Action::UpdateXrSpaceLocations(const OpenXRVk::Device& device, XrTime predictedDisplayTime, AZStd::vector& xrViews) - // { - // const auto thisDevice = static_cast(GetDescriptor().m_device.get()); - // if (thisDevice != &device) - // { - // return false; - // } - // - // auto& rawControllerData = m_xrControllerImpl->GetRawState(); - // const auto session = static_cast(GetDescriptor().m_session.get()); - // XrSession xrSession = session->GetXrSession(); - // XrSpace xrBaseSpaceForVisualization = session->GetXrSpace(session->GetBaseSpaceTypeForVisualization()); - // XrSpace xrBaseSpaceForJoysticks = session->GetXrSpace(session->GetBaseSpaceTypeForControllers()); - // - // // Update poses - // for (const auto hand : { XR::Side::Left, XR::Side::Right }) - // { - // XrActionStateGetInfo getInfo{}; - // getInfo.type = XR_TYPE_ACTION_STATE_GET_INFO; - // getInfo.action = GetPoseAction(static_cast(hand)); - // - // XrActionStatePose poseState{}; - // poseState.type = XR_TYPE_ACTION_STATE_POSE; - // - // XrResult result = xrGetActionStatePose(xrSession, &getInfo, &poseState); - // WARN_IF_UNSUCCESSFUL(result); - // m_handActive[static_cast(hand)] = poseState.isActive; - // - // LocateControllerSpace(predictedDisplayTime, xrBaseSpaceForJoysticks, static_cast(hand)); - // } - // - // // Cache 3d location information - // for (AZ::u32 i = 0; i < static_cast(SpaceType::Count); i++) - // { - // const auto spaceType = static_cast(i); - // LocateVisualizedSpace(predictedDisplayTime, session->GetXrSpace(spaceType), - // xrBaseSpaceForVisualization, spaceType); - // } - // - // rawControllerData.m_leftPositionState = AzPositionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Left)].pose); - // rawControllerData.m_rightPositionState = AzPositionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Right)].pose); - // - // rawControllerData.m_leftOrientationState = AzQuaternionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Left)].pose); - // rawControllerData.m_rightOrientationState = AzQuaternionFromXrPose(m_handSpaceLocation[static_cast(XR::Side::Right)].pose); - // - // if (LocateEyeViews(predictedDisplayTime, xrViews)) - // { - // //! Time to notify the engine that we have new poses. - // const auto& xrSpaceLocationHeadToBase = m_xrVisualizedSpaceLocations[OpenXRVk::SpaceType::View]; - // const auto baseToHeadTm = AzTransformFromXrPose(xrSpaceLocationHeadToBase.pose); - // const auto headToLeftEyeTm = AzTransformFromXrPose(xrViews[0].pose); - // const auto headToRightEyeTm = AzTransformFromXrPose(xrViews[1].pose); - // - // AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, - // baseToHeadTm, headToLeftEyeTm, headToRightEyeTm); - // - // return true; - // } - // - // return false; - // } - // - // bool Action::LocateEyeViews(XrTime predictedDisplayTime, AZStd::vector& xrViews) - // { - // const auto session = static_cast(GetDescriptor().m_session.get()); - // XrSession xrSession = session->GetXrSession(); - // const auto xrVkInstance = static_cast(GetDescriptor().m_instance.get()); - // - // // Let's get the FOV data, which for most practical purposes it is always the same - // // across all frames. But most importantly we need to get the location of each Eye relative to the View Space pose. - // - // Space* xrSpace = static_cast(session->GetSpace()); - // - // XrViewState viewState{ XR_TYPE_VIEW_STATE }; - // uint32_t viewCapacityInput = aznumeric_cast(xrViews.size()); - // uint32_t viewCountOutput = 0; - // - // XrViewLocateInfo viewLocateInfo{ XR_TYPE_VIEW_LOCATE_INFO }; - // viewLocateInfo.viewConfigurationType = xrVkInstance->GetViewConfigType(); - // viewLocateInfo.displayTime = predictedDisplayTime; - // viewLocateInfo.space = xrSpace->GetXrSpace(OpenXRVk::SpaceType::View); - // - // XrResult result = xrLocateViews(xrSession, &viewLocateInfo, &viewState, viewCapacityInput, &viewCountOutput, xrViews.data()); - // ASSERT_IF_UNSUCCESSFUL(result); - // - // if ((viewState.viewStateFlags & XR_VIEW_STATE_POSITION_VALID_BIT) == 0 || - // (viewState.viewStateFlags & XR_VIEW_STATE_ORIENTATION_VALID_BIT) == 0) - // { - // //There is no valid tracking poses for the views - // return false; - // } - // - // AZ_Error(LogName, viewCountOutput == viewCapacityInput, "Size mismatch between xrLocateViews %i and xrEnumerateViewConfigurationViews %i", viewCountOutput, viewCapacityInput); - // - // return (viewCountOutput == viewCapacityInput); - // } - // - // - // void Action::LocateControllerSpace(XrTime predictedDisplayTime, XrSpace baseSpace, AZ::u32 handIndex) - // { - // XrSpaceLocation spaceLocation{}; - // spaceLocation.type = XR_TYPE_SPACE_LOCATION; - // if (const XrResult result = xrLocateSpace(m_handSpace[handIndex], baseSpace, predictedDisplayTime, &spaceLocation); - // result == XR_SUCCESS) - // { - // if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && - // (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) - // { - // m_handSpaceLocation[handIndex] = spaceLocation; - // } - // } - // } - // - // void Action::LocateVisualizedSpace(XrTime predictedDisplayTime, XrSpace space, XrSpace baseSpace, OpenXRVk::SpaceType visualizedSpaceType) - // { - // XrSpaceLocation spaceLocation{}; - // spaceLocation.type = XR_TYPE_SPACE_LOCATION; - // if (const XrResult result = xrLocateSpace(space, baseSpace, predictedDisplayTime, &spaceLocation); - // result == XR_SUCCESS) - // { - // if ((spaceLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && - // (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) - // { - // m_xrVisualizedSpaceLocations[static_cast(visualizedSpaceType)] = spaceLocation; - // } - // } - // } - // - // AZ::RHI::ResultCode Action::GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData, bool convertToO3de) const - // { - // if (handIndex < AZStd::size(m_handSpaceLocation)) - // { - // outPoseData.m_orientation = AzQuaternionFromXrPose(m_handSpaceLocation[handIndex].pose, convertToO3de); - // outPoseData.m_position = AzPositionFromXrPose(m_handSpaceLocation[handIndex].pose, convertToO3de); - // return AZ::RHI::ResultCode::Success; - // } - // return AZ::RHI::ResultCode::Fail; - // } - // - // AZ::RHI::ResultCode Action::GetControllerTransform(AZ::u32 handIndex, AZ::Transform& outTransform, bool convertToO3de) const - // { - // if (handIndex < AZStd::size(m_handSpaceLocation)) - // { - // outTransform = AzTransformFromXrPose(m_handSpaceLocation[handIndex].pose, convertToO3de); - // outTransform.SetUniformScale(m_handScale[handIndex]); - // return AZ::RHI::ResultCode::Success; - // } - // return AZ::RHI::ResultCode::Fail; - // } - // - // AZ::RHI::ResultCode Action::GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType, AZ::RPI::PoseData& outPoseData, bool convertToO3de) const - // { - // const auto spaceIndex = static_cast(visualizedSpaceType); - // if (spaceIndex < AZStd::size(m_xrVisualizedSpaceLocations)) - // { - // outPoseData.m_orientation = AzQuaternionFromXrPose(m_xrVisualizedSpaceLocations[spaceIndex].pose, convertToO3de); - // outPoseData.m_position = AzPositionFromXrPose(m_xrVisualizedSpaceLocations[spaceIndex].pose, convertToO3de); - // return AZ::RHI::ResultCode::Success; - // } - // return AZ::RHI::ResultCode::Fail; - // } - // - // AZ::RHI::ResultCode Action::GetVisualizedSpaceTransform(OpenXRVk::SpaceType visualizedSpaceType, AZ::Transform& outTransform, bool convertToO3de) const - // { - // const auto spaceIndex = static_cast(visualizedSpaceType); - // if (spaceIndex < AZStd::size(m_xrVisualizedSpaceLocations)) - // { - // outTransform = AzTransformFromXrPose(m_xrVisualizedSpaceLocations[spaceIndex].pose, convertToO3de); - // return AZ::RHI::ResultCode::Success; - // } - // return AZ::RHI::ResultCode::Fail; - // } - // - // float Action::GetControllerScale(AZ::u32 handIndex) const - // { - // return m_handScale[handIndex]; - // } - // - // XrAction Action::GetSqueezeAction(AZ::u32 handIndex) const - // { - // return (handIndex == static_cast(XR::Side::Left)) - // ? GetAction(AzFramework::InputDeviceXRController::Trigger::LGrip) - // : GetAction(AzFramework::InputDeviceXRController::Trigger::RGrip); - // } - // - // XrAction Action::GetPoseAction(AZ::u32 handIndex) const - // { - // return (handIndex == static_cast(XR::Side::Left)) - // ? GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::LPos) - // : GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::RPos); - // } - // - // XrAction Action::GetVibrationAction() const - // { - // return m_hapticAction; - // } - // - // XrAction Action::GetQuitAction() const - // { - // return GetAction(AzFramework::InputDeviceXRController::Button::Home); - // } - // - // bool Action::GetButtonState(const AzFramework::InputChannelId& channelId) const - // { - // const auto& state = m_xrControllerImpl->GetRawState(); - // return state.GetDigitalButtonState(channelId); - // } - // - // bool Action::GetXButtonState() const - // { - // return GetButtonState(AzFramework::InputDeviceXRController::Button::X); - // } - // - // bool Action::GetYButtonState() const - // { - // return GetButtonState(AzFramework::InputDeviceXRController::Button::Y); - // } - // - // bool Action::GetAButtonState() const - // { - // return GetButtonState(AzFramework::InputDeviceXRController::Button::A); - // } - // - // bool Action::GetBButtonState() const - // { - // return GetButtonState(AzFramework::InputDeviceXRController::Button::B); - // } - // - // float Action::GetXJoyStickState(AZ::u32 handIndex) const - // { - // const auto& state = m_xrControllerImpl->GetRawState(); - // return (handIndex == static_cast(XR::Side::Left)) - // ? state.m_leftThumbStickXState - // : state.m_rightThumbStickXState; - // } - // - // float Action::GetYJoyStickState(AZ::u32 handIndex) const - // { - // const auto& state = m_xrControllerImpl->GetRawState(); - // return (handIndex == static_cast(XR::Side::Left)) - // ? state.m_leftThumbStickYState - // : state.m_rightThumbStickYState; - // } - // - // float Action::GetSqueezeState(AZ::u32 handIndex) const - // { - // const auto& state = m_xrControllerImpl->GetRawState(); - // return (handIndex == static_cast(XR::Side::Left)) - // ? state.m_leftGripState - // : state.m_rightGripState; - // } - // - // float Action::GetTriggerState(AZ::u32 handIndex) const - // { - // const auto& state = m_xrControllerImpl->GetRawState(); - // return (handIndex == static_cast(XR::Side::Left)) - // ? state.m_leftTriggerState - // : state.m_rightTriggerState; - // } - } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index d904c3b82..a771a74a3 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -54,7 +54,11 @@ namespace OpenXRVk AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) const override; AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const override; AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) const override; + + AZ::Outcome SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) override; + const AZStd::string& GetBaseVisualizedSpaceForPoseActions() const override; AZ::Outcome GetActionStatePose(ActionHandle actionHandle) const override; + AZ::Outcome GetActionStatePoseWithVelocities(ActionHandle actionHandle) const override; AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) override; @@ -108,7 +112,9 @@ namespace OpenXRVk XrSession m_xrSession = XR_NULL_HANDLE; // Updated each time SyncActions is called. XrTime m_predictedDisplaytime; - XrSpace m_baseSpace; + + AZStd::string m_baseVisualizedSpaceName; + XrSpace m_xrBaseVisualizedSpace = XR_NULL_HANDLE; //! Each actionSet in this list is guaranteed to contain at least one valid action. AZStd::vector m_actionSets; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp index cce38a9f7..cceacda5b 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp @@ -73,6 +73,11 @@ namespace OpenXRVk return true; } + void VisualizedSpacesManager::ResetSpaces() + { + AZ_Error(LogName, false, "FIXME! %s", __FUNCTION__); + } + ///////////////////////////////////////////////// /// OpenXRVisualizedSpacesInterface overrides AZStd::vector VisualizedSpacesManager::GetVisualizedSpaceNames() const @@ -139,6 +144,16 @@ namespace OpenXRVk return AZ::Success(true); } + const void * VisualizedSpacesManager::GetVisualizedSpaceNativeHandle(const AZStd::string& spaceName) const + { + const auto spaceItor = m_spaces.find(spaceName); + if (spaceItor == m_spaces.end()) + { + return nullptr; + } + return static_cast(spaceItor->second.m_xrSpace); + } + AZ::Outcome VisualizedSpacesManager::GetVisualizedSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h index 78d1f7567..b2a3c0435 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h @@ -30,6 +30,10 @@ namespace OpenXRVk //! Called by the Session each tick. bool UpdateViewSpacePoseAndEyeViewPoses(XrTime predictedDisplayTime); + // Spaces are reset every time the proximity sensor turns off, or the user wears the headset + // when the proximity sensor is ON. + void ResetSpaces(); + ///////////////////////////////////////////////// /// OpenXRVisualizedSpacesInterface overrides AZStd::vector GetVisualizedSpaceNames() const override; @@ -37,6 +41,7 @@ namespace OpenXRVk const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) override; AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) override; + const void * GetVisualizedSpaceNativeHandle(const AZStd::string& spaceName) const override; AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const override; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index ba4a34182..54692c30e 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -19,8 +19,10 @@ #include #include +#include #include "OpenXRActionsManager.h" +#include "OpenXRVisualizedSpacesManager.h" namespace OpenXRVk { @@ -52,18 +54,16 @@ namespace OpenXRVk XrResult result = xrCreateSession(m_xrInstance, &createInfo, &m_session); ASSERT_IF_UNSUCCESSFUL(result); + m_visualizedSpacesMgr = AZStd::make_unique(); + success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); + AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); + m_actionsMgr = AZStd::make_unique(); - bool actionsSuccess = m_actionsMgr->Init(m_xrInstance, m_session); - AZ_Error("OpenXRVk::Session", actionsSuccess, "Failed to instantiate the actions manager"); + bool success = m_actionsMgr->Init(m_xrInstance, m_session); + AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the actions manager"); LogReferenceSpaces(); - //Input* xrVkInput = GetNativeInput(); - //xrVkInput->InitializeActionSpace(m_session); - //xrVkInput->InitializeActionSets(m_session); - - Space* xrVkSpace = static_cast(GetSpace()); - xrVkSpace->CreateVisualizedSpaces(m_session); return ConvertResult(result); } @@ -127,7 +127,7 @@ namespace OpenXRVk // is not wearing the headset. Each time the proximity sensor is disabled or the user // decides to wear the headset, the XrSpaces need to be recreated, otherwise their // poses would be corrupted. - ResetSpaces(); + m_visualizedSpacesMgr->ResetSpaces(); break; } case XR_SESSION_STATE_STOPPING: @@ -159,13 +159,6 @@ namespace OpenXRVk } } - void Session::ResetSpaces() - { - // FIXME! What about action spaces?? - Space* xrVkSpace = static_cast(GetSpace()); - xrVkSpace->ShutdownInternal(); - xrVkSpace->CreateVisualizedSpaces(m_session); - } const XrEventDataBaseHeader* Session::TryReadNextEvent() { @@ -220,16 +213,19 @@ namespace OpenXRVk } case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: { - if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled) - { - Input* xrVkInput = GetNativeInput(); - LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast(XR::Side::Left)), "Squeeze Left"); - LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast(XR::Side::Right)), "Squeeze Right"); - LogActionSourceName(xrVkInput->GetQuitAction(), "Quit"); - LogActionSourceName(xrVkInput->GetPoseAction(static_cast(XR::Side::Left)), "Pose Left"); - LogActionSourceName(xrVkInput->GetPoseAction(static_cast(XR::Side::Right)), "Pose Right"); - LogActionSourceName(xrVkInput->GetVibrationAction(), "Vibrate"); - } + // GALIB FIXME! + AZ_Printf("GALIB", "OpenXRVkSession FIXME XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED\n"); + + //if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled) + //{ + // Input* xrVkInput = GetNativeInput(); + // LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast(XR::Side::Left)), "Squeeze Left"); + // LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast(XR::Side::Right)), "Squeeze Right"); + // LogActionSourceName(xrVkInput->GetQuitAction(), "Quit"); + // LogActionSourceName(xrVkInput->GetPoseAction(static_cast(XR::Side::Left)), "Pose Left"); + // LogActionSourceName(xrVkInput->GetPoseAction(static_cast(XR::Side::Right)), "Pose Right"); + // LogActionSourceName(xrVkInput->GetVibrationAction(), "Vibrate"); + //} break; } case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: @@ -437,6 +433,7 @@ namespace OpenXRVk void Session::UpdateXrSpaceLocations([[maybe_unused]] const OpenXRVk::Device& device, [[maybe_unused]] XrTime predictedDisplayTime, [[maybe_unused]] AZStd::vector& xrViews) { m_actionsMgr->SyncActions(); + //GetNativeInput()->UpdateXrSpaceLocations(device, predictedDisplayTime, xrViews); } } From 2c5c9baf05b9ca203d6fce03e1eed5c9ea98501c Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 18 Jan 2024 09:42:34 -0600 Subject: [PATCH 08/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVisualizedSpacesInterface.h | 19 +++-- .../Code/Include/OpenXRVk/OpenXRVkDevice.h | 6 +- .../Code/Include/OpenXRVk/OpenXRVkSession.h | 6 +- .../Code/Source/OpenXRActionsManager.cpp | 15 ++-- .../Code/Source/OpenXRActionsManager.h | 2 +- .../Source/OpenXRVisualizedSpacesManager.cpp | 73 +++++++++++++++---- .../Source/OpenXRVisualizedSpacesManager.h | 14 +++- Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp | 49 +++++-------- Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp | 16 ++-- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 24 +++++- .../Code/Source/OpenXRVkSwapChain.cpp | 4 - 11 files changed, 143 insertions(+), 85 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h index 89aa95ffc..547fddde6 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h @@ -13,6 +13,7 @@ #include #include +#include namespace OpenXRVk { @@ -71,6 +72,16 @@ namespace OpenXRVk // Some headsets like the Varjo support Quad Views and the eye counts will be 4. virtual uint32_t GetViewCount() const = 0; + // Pose of a view(aka eye) relative to the View Space (aka Head) Pose. + // For AR applications running on a Phone (aka Mono view configuration) the Eye View Pose + // is centered exactly where the View Space Centroid is located, so calling this function wouldn't + // make much sense because it'll return an Identity transform. + virtual const AZ::Transform& GetViewPose(uint32_t eyeIndex) const = 0; + + virtual const AZ::RPI::FovData& GetViewFovData(uint32_t eyeIndex) const = 0; + + virtual const AZStd::vector& GetViewPoses() const = 0; + //! Forces updating the cached pose and projection data for all Views. //! Each frame, all view (aka eye) poses and projections are updated automatically, making this function optional to use. //! By default the caller simply gets the per-frame cached version, which should be fine for most applications. @@ -79,14 +90,6 @@ namespace OpenXRVk //! Instead the prediction gets increasingly accurate as the function is called closer to the //! given time for which a prediction is made" virtual void ForceViewPosesCacheUpdate() = 0; - - // Pose of a view(aka eye) relative to the View Space (aka Head) Pose. - // For AR applications running on a Phone (aka Mono view configuration) the Eye View Pose - // is centered exactly where the View Space Centroid is located, so calling this function wouldn't - // make much sense because it'll return an Identity transform. - virtual const AZ::Transform& GetViewPose(uint32_t eyeIndex) const = 0; - - virtual const AZ::RPI::FovData& GetViewFovData(uint32_t eyeIndex) const = 0; }; using OpenXRVisualizedSpacesInterface = AZ::Interface; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h index 752894ce0..7c6cf6c30 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h @@ -31,6 +31,8 @@ namespace OpenXRVk // Create the xr specific native device object and populate the XRDeviceDescriptor with it. AZ::RHI::ResultCode InitDeviceInternal(AZ::RHI::XRDeviceDescriptor* instanceDescriptor) override; + + //! FIXME: Remove me! //! Get the Fov data of the view specified by view index AZ::RHI::ResultCode GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const override; //! Get the Pose data of the view specified by view index @@ -49,9 +51,6 @@ namespace OpenXRVk //! Returns the graphic binding for a hardware queue const AZ::Vulkan::XRDeviceDescriptor::GraphicsBinding& GetGraphicsBinding(AZ::RHI::HardwareQueueClass queueClass) const; - //! Reserve space for appropriate number of views - void InitXrViews(uint32_t numViews); - //! Get the anticipated display XrTime for the next application-generated frame. XrTime GetPredictedDisplayTime() const; @@ -78,6 +77,5 @@ namespace OpenXRVk AZStd::vector m_xrLayers; XrCompositionLayerProjection m_xrLayer{ XR_TYPE_COMPOSITION_LAYER_PROJECTION }; AZStd::vector m_projectionLayerViews; - AZStd::vector m_views; }; } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h index d0efd5967..e97929cd8 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h @@ -43,11 +43,15 @@ namespace OpenXRVk //! Return the Xrspace related to the SpaceType enum XrSpace GetXrSpace(SpaceType spaceType) const; + const AZStd::vector& GetXrViews() const; + + XrSpace GetViewSpaceXrSpace() const; + //////////////////////////////////////////////////////////////////////////////////////////// //! Called by a Device when the predicted display time has been updated (typically //! the device updates the predicted display time during BeginFrame). //! See OpenXRVkInput.h UpdateXrSpaceLocations(...) for more details. - void UpdateXrSpaceLocations(const OpenXRVk::Device& device, XrTime predictedDisplayTime, AZStd::vector& xrViews); + void OnBeginFrame(XrTime predictedDisplayTime); //! Setters and Getters for the base spaces that will be used //! when calling xrLocateSpace(). diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index 3686eb7fc..b67aa673e 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -97,7 +97,7 @@ namespace OpenXRVk return true; } - bool ActionsManager::SyncActions(XrTime predictedDisplayTime, XrSpace baseSpace) + bool ActionsManager::SyncActions(XrTime predictedDisplayTime) { if (m_xrActiveActionSets.empty()) { @@ -106,7 +106,6 @@ namespace OpenXRVk } m_predictedDisplaytime = predictedDisplayTime; - m_baseSpace = baseSpace; XrActionsSyncInfo syncInfo{ XR_TYPE_ACTIONS_SYNC_INFO }; syncInfo.countActiveActionSets = aznumeric_cast(m_xrActiveActionSets.size()); @@ -476,14 +475,16 @@ namespace OpenXRVk return AZ::Failure("The OpenXRVisualizedSpacesInterface doesn't exist!"); } - void* opaqueXrSpace = visualizedSpacesIface->GetVisualizedSpaceNativeHandle(visualizedSpaceName); + const void* opaqueXrSpace = visualizedSpacesIface->GetVisualizedSpaceNativeHandle(visualizedSpaceName); if (!opaqueXrSpace) { - return AZ::Failure("Visualized space with name [%s] doesn't exist. Will keep the current base space named [%s]", - visualizedSpaceName.c_str(), m_baseVisualizedSpaceName.c_str()); + return AZ::Failure( + AZStd::string::format("Visualized space with name [%s] doesn't exist. Will keep the current base space named [%s]", + visualizedSpaceName.c_str(), m_baseVisualizedSpaceName.c_str()) + ); } m_baseVisualizedSpaceName = visualizedSpaceName; - m_xrBaseVisualizedSpace = reinterpret_cast(opaqueXrSpace); + m_xrBaseVisualizedSpace = reinterpret_cast(const_cast(opaqueXrSpace)); return AZ::Success(true); } @@ -533,7 +534,7 @@ namespace OpenXRVk XrSpaceVelocity spaceVelocity{ XR_TYPE_SPACE_VELOCITY }; XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION, &spaceVelocity}; - XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_baseSpace, m_predictedDisplaytime, &spaceLocation); + XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseVisualizedSpace, m_predictedDisplaytime, &spaceLocation); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index a771a74a3..eff047bdc 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -39,7 +39,7 @@ namespace OpenXRVk bool Init(XrInstance xrInstance, XrSession xrSession); //! Called by the Session each tick. - bool SyncActions(XrTime predictedDisplayTime, XrSpace baseSpace); + bool SyncActions(XrTime predictedDisplayTime); ///////////////////////////////////////////////// /// OpenXRActionsInterface overrides diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp index cceacda5b..65ba52103 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp @@ -20,8 +20,10 @@ namespace OpenXRVk m_xrInstance = xrInstance; m_xrSession = xrSession; m_xrViewConfigurationType = xrViewConfigurationType; - m_xrViews.resize_no_construct(numEyeViews); + m_xrViews.resize(numEyeViews, { XR_TYPE_VIEW }); + m_eyeViewPoses.resize_no_construct(numEyeViews); + m_eyeViewFovDatas.resize_no_construct(numEyeViews); m_viewSpacePose = AZ::Transform::Identity(); for (uint32_t eyeViewIdx = 0; eyeViewIdx < numEyeViews; eyeViewIdx++) { @@ -56,7 +58,7 @@ namespace OpenXRVk return true; } - bool VisualizedSpacesManager::UpdateViewSpacePoseAndEyeViewPoses(XrTime predictedDisplayTime) + bool VisualizedSpacesManager::SyncViews(XrTime predictedDisplayTime) { m_predictedDisplaytime = predictedDisplayTime; @@ -78,6 +80,16 @@ namespace OpenXRVk AZ_Error(LogName, false, "FIXME! %s", __FUNCTION__); } + const AZStd::vector& VisualizedSpacesManager::GetXrViews() const + { + return m_xrViews; + } + + XrSpace VisualizedSpacesManager::GetViewSpaceXrSpace() const + { + return m_viewSpace->m_xrSpace; + } + ///////////////////////////////////////////////// /// OpenXRVisualizedSpacesInterface overrides AZStd::vector VisualizedSpacesManager::GetVisualizedSpaceNames() const @@ -107,6 +119,9 @@ namespace OpenXRVk ); } + VisualizedSpace visualizedSpace{ spaceName, poseInReferenceSpace, newXrSpace }; + m_spaces.emplace(spaceName, AZStd::move(visualizedSpace)); + return AZ::Success(true); } @@ -216,6 +231,40 @@ namespace OpenXRVk return aznumeric_cast(m_eyeViewPoses.size()); } + const AZ::Transform& VisualizedSpacesManager::GetViewPose(uint32_t eyeIndex) const + { + if (eyeIndex >= m_eyeViewPoses.size()) + { + // Debug and Profile builds shoud crash here. + AZ_Assert(false, "Can't get View Pose because [%u] is out of bounds", eyeIndex); + // In Release build we'd see this message. + AZ_Error(LogName, false, "Can't get View Pose because [%u] is out of bounds", eyeIndex); + return AZ::Transform::Identity(); + } + + return m_eyeViewPoses[eyeIndex]; + } + + const AZ::RPI::FovData& VisualizedSpacesManager::GetViewFovData(uint32_t eyeIndex) const + { + if (eyeIndex >= m_eyeViewPoses.size()) + { + // Debug and Profile builds shoud crash here. + AZ_Assert(false, "Can't get View FovData because [%u] is out of bounds", eyeIndex); + // In Release build we'd see this message. + AZ_Error(LogName, false, "Can't get View FovData because [%u] is out of bounds", eyeIndex); + static const AZ::RPI::FovData ZeroFovData{}; + return ZeroFovData; + } + + return m_eyeViewFovDatas[eyeIndex]; + } + + const AZStd::vector& VisualizedSpacesManager::GetViewPoses() const + { + return m_eyeViewPoses; + } + void VisualizedSpacesManager::ForceViewPosesCacheUpdate() { XrViewState viewState{ XR_TYPE_VIEW_STATE }; @@ -241,22 +290,14 @@ namespace OpenXRVk for (size_t viewIdx = 0; viewIdx < m_xrViews.size(); viewIdx++) { m_eyeViewPoses[viewIdx] = AzTransformFromXrPose(m_xrViews[viewIdx].pose); + auto& fovData = m_eyeViewFovDatas[viewIdx]; + const auto& xrFov = m_xrViews[viewIdx].fov; + fovData.m_angleLeft = xrFov.angleLeft; + fovData.m_angleRight = xrFov.angleRight; + fovData.m_angleUp = xrFov.angleUp; + fovData.m_angleDown = xrFov.angleDown; } } - - const AZ::Transform& VisualizedSpacesManager::GetViewPose(uint32_t eyeIndex) const - { - if (eyeIndex >= m_eyeViewPoses.size()) - { - // Debug and Profile builds shoud crash here. - AZ_Assert(false, "Can't get View Pose because [%u] is out of bounds", eyeIndex); - // In Release build we'd see this message. - AZ_Error(LogName, false, "Can't get View Pose because [%u] is out of bounds", eyeIndex); - return AZ::Transform::Identity(); - } - - return m_eyeViewPoses[eyeIndex]; - } /// OpenXRVisualizedSpacesInterface overrides ///////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h index b2a3c0435..c83cb437f 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h @@ -28,12 +28,16 @@ namespace OpenXRVk bool Init(XrInstance xrInstance, XrSession xrSession, XrViewConfigurationType xrViewConfigurationType, uint32_t numEyeViews); //! Called by the Session each tick. - bool UpdateViewSpacePoseAndEyeViewPoses(XrTime predictedDisplayTime); + bool SyncViews(XrTime predictedDisplayTime); // Spaces are reset every time the proximity sensor turns off, or the user wears the headset // when the proximity sensor is ON. void ResetSpaces(); + const AZStd::vector& GetXrViews() const; + + XrSpace GetViewSpaceXrSpace() const; + ///////////////////////////////////////////////// /// OpenXRVisualizedSpacesInterface overrides AZStd::vector GetVisualizedSpaceNames() const override; @@ -51,8 +55,11 @@ namespace OpenXRVk const AZ::Transform& GetViewSpacePose() const override; uint32_t GetViewCount() const override; - void ForceViewPosesCacheUpdate() override; const AZ::Transform& GetViewPose(uint32_t eyeIndex) const override; + const AZ::RPI::FovData& GetViewFovData(uint32_t eyeIndex) const override; + const AZStd::vector& GetViewPoses() const override; + + void ForceViewPosesCacheUpdate() override; /// OpenXRVisualizedSpacesInterface overrides ///////////////////////////////////////////////// @@ -68,6 +75,8 @@ namespace OpenXRVk struct VisualizedSpace { AZStd::string m_name; + //! We shave this transform in case we have to reset the spaces. + AZ::Transform m_offsetPose; // Runtime data XrSpace m_xrSpace; }; @@ -83,6 +92,7 @@ namespace OpenXRVk AZ::Transform m_viewSpacePose; //! The following poses are always relative to @m_viewSpacePose. AZStd::vector m_eyeViewPoses; + AZStd::vector m_eyeViewFovDatas; AZStd::vector m_xrViews; XrSpace CreateXrSpace(XrReferenceSpaceType referenceSpaceType, const AZ::Transform& relativePose); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp index e33454b2e..de407c167 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -56,8 +57,10 @@ namespace OpenXRVk // Now that we have a new predicted display time, the session should be able // to sync actions and locate spaces. // The new predicted display time will be used to calculate XrPoses for the current frame. - session->UpdateXrSpaceLocations(*this, m_frameState.predictedDisplayTime, m_views); - + if (session->IsSessionFocused()) + { + session->OnBeginFrame(m_frameState.predictedDisplayTime); + } //Always return true as we want EndFrame to always be called. return true; } @@ -69,7 +72,6 @@ namespace OpenXRVk Session* session = static_cast(GetSession().get()); Instance* instance = static_cast(GetDescriptor().m_instance.get()); SwapChain* swapChain = static_cast(baseSwapChain.get()); - Space* xrSpace = static_cast(GetSession()->GetSpace()); XrSession xrSession = session->GetXrSession(); for(uint32_t i = 0; i < swapChain->GetNumViews(); i++) @@ -87,7 +89,7 @@ namespace OpenXRVk } } - m_xrLayer.space = xrSpace->GetXrSpace(OpenXRVk::SpaceType::View); + m_xrLayer.space = session->GetViewSpaceXrSpace(); m_xrLayer.viewCount = aznumeric_cast(m_projectionLayerViews.size()); m_xrLayer.views = m_projectionLayerViews.data(); @@ -119,6 +121,7 @@ namespace OpenXRVk XR::SwapChain::View* baseSwapChainView = baseSwapChain->GetView(viewIndex); SwapChain::View* swapChainView = static_cast(baseSwapChainView); XrSwapchain swapChainHandle = swapChainView->GetSwapChainHandle(); + Session* session = static_cast(GetSession().get()); XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO }; auto result = xrAcquireSwapchainImage(swapChainHandle, &acquireInfo, &baseSwapChainView->m_activeImageIndex); @@ -131,11 +134,12 @@ namespace OpenXRVk ASSERT_IF_UNSUCCESSFUL(result); // REMARK: The data in m_views was updated during BeginFrameInternal(), which - // calls session->UpdateXrSpaceLocations(...). - m_projectionLayerViews.resize(m_views.size()); + // calls session->OnBeginFrame(...). + const auto& xrViews = session->GetXrViews(); + m_projectionLayerViews.resize(xrViews.size()); m_projectionLayerViews[viewIndex] = { XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW }; - m_projectionLayerViews[viewIndex].pose = m_views[viewIndex].pose; - m_projectionLayerViews[viewIndex].fov = m_views[viewIndex].fov; + m_projectionLayerViews[viewIndex].pose = xrViews[viewIndex].pose; + m_projectionLayerViews[viewIndex].fov = xrViews[viewIndex].fov; m_projectionLayerViews[viewIndex].subImage.swapchain = swapChainHandle; m_projectionLayerViews[viewIndex].subImage.imageRect.offset = { 0, 0 }; m_projectionLayerViews[viewIndex].subImage.imageRect.extent = { static_cast(swapChainView->GetWidth()), @@ -148,13 +152,6 @@ namespace OpenXRVk return m_frameState.shouldRender == XR_TRUE; } - void Device::InitXrViews(uint32_t numViews) - { - // Create and cache view buffer for xrLocateViews later. - m_views.clear(); - m_views.resize(numViews, { XR_TYPE_VIEW }); - } - VkDevice Device::GetNativeDevice() const { return m_xrVkDevice; @@ -170,27 +167,20 @@ namespace OpenXRVk return m_xrQueueBinding[static_cast(queueClass)]; } - AZ::RHI::ResultCode Device::GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const + AZ::RHI::ResultCode Device::GetViewFov([[maybe_unused]] AZ::u32 viewIndex, [[maybe_unused]] AZ::RPI::FovData& outFovData) const { - if(viewIndex < m_views.size()) - { - outFovData.m_angleLeft = m_views[viewIndex].fov.angleLeft; - outFovData.m_angleRight = m_views[viewIndex].fov.angleRight; - outFovData.m_angleUp = m_views[viewIndex].fov.angleUp; - outFovData.m_angleDown = m_views[viewIndex].fov.angleDown; + if (auto spacesIface = OpenXRVisualizedSpacesInterface::Get(); + spacesIface != nullptr) + { + outFovData = spacesIface->GetViewFovData(viewIndex); return AZ::RHI::ResultCode::Success; } return AZ::RHI::ResultCode::Fail; } - AZ::RHI::ResultCode Device::GetViewPose(AZ::u32 viewIndex, AZ::RPI::PoseData& outPoseData) const + AZ::RHI::ResultCode Device::GetViewPose([[maybe_unused]] AZ::u32 viewIndex, [[maybe_unused]] AZ::RPI::PoseData& outPoseData) const { - if (viewIndex < m_views.size()) - { - outPoseData.m_orientation = AzQuaternionFromXrPose(m_views[viewIndex].pose); - outPoseData.m_position = AzPositionFromXrPose(m_views[viewIndex].pose); - return AZ::RHI::ResultCode::Success; - } + AZ_Assert(false, "Don't call this function!"); return AZ::RHI::ResultCode::Fail; } @@ -202,7 +192,6 @@ namespace OpenXRVk void Device::ShutdownInternal() { m_projectionLayerViews.clear(); - m_views.clear(); m_xrLayers.clear(); m_xrVkDevice = VK_NULL_HANDLE; m_xrVkPhysicalDevice = VK_NULL_HANDLE; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp index fe1edf509..336996bdd 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp @@ -394,14 +394,14 @@ namespace OpenXRVk if (LocateEyeViews(predictedDisplayTime, xrViews)) { - //! Time to notify the engine that we have new poses. - const auto& xrSpaceLocationHeadToBase = m_xrVisualizedSpaceLocations[OpenXRVk::SpaceType::View]; - const auto baseToHeadTm = AzTransformFromXrPose(xrSpaceLocationHeadToBase.pose); - const auto headToLeftEyeTm = AzTransformFromXrPose(xrViews[0].pose); - const auto headToRightEyeTm = AzTransformFromXrPose(xrViews[1].pose); - - AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, - baseToHeadTm, headToLeftEyeTm, headToRightEyeTm); + // //! Time to notify the engine that we have new poses. + // const auto& xrSpaceLocationHeadToBase = m_xrVisualizedSpaceLocations[OpenXRVk::SpaceType::View]; + // const auto baseToHeadTm = AzTransformFromXrPose(xrSpaceLocationHeadToBase.pose); + // const auto headToLeftEyeTm = AzTransformFromXrPose(xrViews[0].pose); + // const auto headToRightEyeTm = AzTransformFromXrPose(xrViews[1].pose); + // + // AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, + // baseToHeadTm, headToLeftEyeTm, headToRightEyeTm); return true; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index 54692c30e..4286e3792 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -55,11 +55,11 @@ namespace OpenXRVk ASSERT_IF_UNSUCCESSFUL(result); m_visualizedSpacesMgr = AZStd::make_unique(); - success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); + bool success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); m_actionsMgr = AZStd::make_unique(); - bool success = m_actionsMgr->Init(m_xrInstance, m_session); + success = m_actionsMgr->Init(m_xrInstance, m_session); AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the actions manager"); LogReferenceSpaces(); @@ -397,6 +397,16 @@ namespace OpenXRVk return space->GetXrSpace(spaceType); } + const AZStd::vector& Session::GetXrViews() const + { + return m_visualizedSpacesMgr->GetXrViews(); + } + + XrSpace Session::GetViewSpaceXrSpace() const + { + return m_visualizedSpacesMgr->GetViewSpaceXrSpace(); + } + bool Session::IsSessionRunning() const { return m_sessionRunning; @@ -430,9 +440,15 @@ namespace OpenXRVk return static_cast(GetInput()); } - void Session::UpdateXrSpaceLocations([[maybe_unused]] const OpenXRVk::Device& device, [[maybe_unused]] XrTime predictedDisplayTime, [[maybe_unused]] AZStd::vector& xrViews) + void Session::OnBeginFrame(XrTime predictedDisplayTime) { - m_actionsMgr->SyncActions(); + m_actionsMgr->SyncActions(predictedDisplayTime); + m_visualizedSpacesMgr->SyncViews(predictedDisplayTime); + + //Notify the rest of the engine. + AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, + m_visualizedSpacesMgr->GetViewSpacePose(), + m_visualizedSpacesMgr->GetViewPoses()); //GetNativeInput()->UpdateXrSpaceLocations(device, predictedDisplayTime, xrViews); } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp index 1e6eb9544..8b50a41e3 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp @@ -86,7 +86,6 @@ namespace OpenXRVk { Instance* xrVkInstance = static_cast(GetDescriptor().m_instance.get()); Session* xrVkSession = static_cast(GetDescriptor().m_session.get()); - Device* xrDevice = static_cast(GetDescriptor().m_device.get()); XrInstance xrInstance = xrVkInstance->GetXRInstance(); XrSystemId xrSystemId = xrVkInstance->GetXRSystemId(); XrSession xrSession = xrVkSession->GetXrSession(); @@ -122,9 +121,6 @@ namespace OpenXRVk viewConfigType, m_numViews, &m_numViews, m_configViews.data()); WARN_IF_UNSUCCESSFUL(result); - // Create and cache view buffer for xrLocateViews later. - xrDevice->InitXrViews(m_numViews); - // Create the swapchain and get the images. if (m_numViews > 0) { From bd55fb69165c653e91cdd31000a083a893cc1b5f Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:26:23 -0600 Subject: [PATCH 09/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRInteractionProfileBus.h | 1 + .../OpenXRVisualizedSpacesInterface.h | 2 +- .../KHRSimpleProfileSystemComponent.cpp | 22 +++-- .../KHRSimpleProfileSystemComponent.h | 1 + .../Code/Source/OpenXRActionsManager.cpp | 82 ++++++++++++++++++- .../Code/Source/OpenXRActionsManager.h | 2 + Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp | 5 +- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 8 +- .../Code/Source/XRCameraMovementComponent.cpp | 46 +++++++++++ .../Code/Source/XRCameraMovementComponent.h | 1 + 10 files changed, 158 insertions(+), 12 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h index d172d9f9a..c8c3eba22 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h @@ -30,6 +30,7 @@ namespace OpenXRVk // The following functions are called during asset creation time. virtual AZStd::string GetName() const = 0; virtual AZStd::vector GetUserPaths() const = 0; + virtual AZStd::string GetUserTopPath(const AZStd::string& userPathName) const = 0; virtual AZStd::vector GetComponentPaths(const AZStd::string& userPath) const = 0; // ////////////////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h index 547fddde6..ccbd50828 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h @@ -21,7 +21,7 @@ namespace OpenXRVk class IOpenXRVisualizedSpaces { public: - AZ_RTTI(IOpenXRVisualizedSpaces, "{7B163790-BDBE-4C5B-832E-768CF5CDF585}"); + AZ_RTTI(IOpenXRVisualizedSpaces, "{244D24BE-DD6F-430A-8F99-1D24AC1665B6}"); AZ_DISABLE_COPY_MOVE(IOpenXRVisualizedSpaces); IOpenXRVisualizedSpaces() = default; diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp index 9ef15ae3b..4e8343ec1 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp @@ -40,11 +40,11 @@ namespace OpenXRVk }; const AZStd::vector commonPaths = { - {"Select Button", "/input/select/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, - {"Menu Button", "/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT}, - {"Grip Pose", "/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT}, - {"Aim Pose", "/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT}, - {"Vibration", "/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT}, + {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, }; m_componentPaths[LeftHand] = commonPaths; m_componentPaths[RightHand] = commonPaths; @@ -76,6 +76,18 @@ namespace OpenXRVk return retList; } + AZStd::string KHRSimpleProfileSystemComponent::GetUserTopPath(const AZStd::string& userPathName) const + { + for (const auto& openxrPath : m_userPaths) + { + if (openxrPath.m_displayName == userPathName) + { + return openxrPath.m_xrRelativePath; + } + } + return AZStd::string(""); + } + AZStd::vector KHRSimpleProfileSystemComponent::GetComponentPaths(const AZStd::string& userPath) const { if (!m_componentPaths.contains(userPath)) diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h index f86911020..7bd7be9ff 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h @@ -59,6 +59,7 @@ namespace OpenXRVk //! Create OpenXRVk::Instance object AZStd::string GetName() const override; AZStd::vector GetUserPaths() const override; + AZStd::string GetUserTopPath(const AZStd::string& userPathName) const override; AZStd::vector GetComponentPaths(const AZStd::string& userPath) const override; OpenXRInteractionProfile::ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const override; AZStd::string GetInteractionProviderPath() const override; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index b67aa673e..359a1d9a5 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -316,6 +316,52 @@ namespace OpenXRVk return true; } + void ActionsManager::LogCurrentInteractionProfile() + { + OpenXRInteractionProfileBus::EnumerateHandlers( + [this](OpenXRInteractionProfile* handler) -> bool + { + auto userPathStrs = handler->GetUserPaths(); + auto profileName = handler->GetName(); + AZ_Printf(LogName, "Visiting user paths for interaction profile [%s]\n", profileName.c_str()); + for (const auto& userPathStr : userPathStrs) + { + const auto topPathstr = handler->GetUserTopPath(userPathStr); + XrPath xrPath; + XrResult result = xrStringToPath(m_xrInstance, topPathstr.c_str(), &xrPath); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to get xrPath for user top path [%s]", topPathstr.c_str()); + continue; + } + XrInteractionProfileState profileStateOut{ XR_TYPE_INTERACTION_PROFILE_STATE }; + result = xrGetCurrentInteractionProfile( + m_xrSession, xrPath, &profileStateOut); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to get profile state for user top path [%s]", topPathstr.c_str()); + continue; + } + if (profileStateOut.interactionProfile == XR_NULL_PATH) + { + AZ_Printf(LogName, "Got an NULL Interaction Profile for [%s].\n", topPathstr.c_str()); + continue; + } + constexpr uint32_t numBytes = 256; + char pathAsCStr[numBytes]; + uint32_t validBytes = 0; + result = xrPathToString(m_xrInstance, profileStateOut.interactionProfile, numBytes, &validBytes, pathAsCStr); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to convert XrPath to string for user top path [%s]", topPathstr.c_str()); + continue; + } + AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", topPathstr.c_str(), pathAsCStr); + } + return true; + }); + } + ///////////////////////////////////////////////// /// OpenXRActionsInterface overrides AZStd::vector ActionsManager::GetAllActionSets() const @@ -417,6 +463,12 @@ namespace OpenXRVk { return AZ::Failure(AZStd::string(GetResultString(result))); } + if (!state.isActive) + { + return AZ::Failure( + AZStd::string::format("Boolean Action [%s] is NOT active.", m_actions[actionIndex].m_name.c_str()) + ); + } return AZ::Success(state.currentState); } @@ -437,6 +489,12 @@ namespace OpenXRVk { return AZ::Failure(AZStd::string(GetResultString(result))); } + if (!state.isActive) + { + return AZ::Failure( + AZStd::string::format("Float Action [%s] is NOT active.", m_actions[actionIndex].m_name.c_str()) + ); + } return AZ::Success(state.currentState); } @@ -457,6 +515,12 @@ namespace OpenXRVk { return AZ::Failure(AZStd::string(GetResultString(result))); } + if (!state.isActive) + { + return AZ::Failure( + AZStd::string::format("Vector2 Action[%s] is NOT active.", m_actions[actionIndex].m_name.c_str()) + ); + } return AZ::Success(AZ::Vector2(state.currentState.x, state.currentState.y)); } @@ -501,8 +565,24 @@ namespace OpenXRVk } const auto actionIndex = actionHandle.GetIndex(); + // First, we need to make sure the Action is active. + XrActionStatePose state{ XR_TYPE_ACTION_STATE_POSE }; + XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO }; + getInfo.action = m_actions[actionIndex].m_xrAction; + XrResult result = xrGetActionStatePose(m_xrSession, &getInfo, &state); + if (IsError(result)) + { + return AZ::Failure(AZStd::string(GetResultString(result))); + } + if (!state.isActive) + { + return AZ::Failure( + AZStd::string::format("Pose Action [%s] is NOT active.", m_actions[actionIndex].m_name.c_str()) + ); + } + XrSpaceLocation spaceLocation {XR_TYPE_SPACE_LOCATION}; - XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseVisualizedSpace, m_predictedDisplaytime, &spaceLocation); + result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseVisualizedSpace, m_predictedDisplaytime, &spaceLocation); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index eff047bdc..f7bedb4a8 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -41,6 +41,8 @@ namespace OpenXRVk //! Called by the Session each tick. bool SyncActions(XrTime predictedDisplayTime); + void LogCurrentInteractionProfile(); + ///////////////////////////////////////////////// /// OpenXRActionsInterface overrides AZStd::vector GetAllActionSets() const override; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp index de407c167..e0c7c2bd2 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp @@ -57,10 +57,7 @@ namespace OpenXRVk // Now that we have a new predicted display time, the session should be able // to sync actions and locate spaces. // The new predicted display time will be used to calculate XrPoses for the current frame. - if (session->IsSessionFocused()) - { - session->OnBeginFrame(m_frameState.predictedDisplayTime); - } + session->OnBeginFrame(m_frameState.predictedDisplayTime); //Always return true as we want EndFrame to always be called. return true; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index 4286e3792..bb464e59b 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -215,6 +215,8 @@ namespace OpenXRVk { // GALIB FIXME! AZ_Printf("GALIB", "OpenXRVkSession FIXME XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED\n"); + m_actionsMgr->LogCurrentInteractionProfile(); + //if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled) //{ @@ -442,7 +444,11 @@ namespace OpenXRVk void Session::OnBeginFrame(XrTime predictedDisplayTime) { - m_actionsMgr->SyncActions(predictedDisplayTime); + if (IsSessionFocused()) + { + // Syncing actions only works if the session is in focused state + m_actionsMgr->SyncActions(predictedDisplayTime); + } m_visualizedSpacesMgr->SyncViews(predictedDisplayTime); //Notify the rest of the engine. diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp index b19b7afc4..91e7775b5 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp @@ -13,7 +13,10 @@ #include #include +#include // GALIB + #include +#include #include #include @@ -105,6 +108,8 @@ namespace OpenXRVk void XRCameraMovementComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint) { + ProcessOpenXRActions(); + AZ::Transform cameraTransform; AZ::TransformBus::EventResult(cameraTransform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM); @@ -159,6 +164,47 @@ namespace OpenXRVk } } + void XRCameraMovementComponent::ProcessOpenXRActions() + { + auto actionsIFace = OpenXRActionsInterface::Get(); + if (!actionsIFace) + { + return; + } + // Button + { + auto actionHandle = actionsIFace->GetActionHandle("my_action_set", "button"); + if (!actionHandle.IsValid()) + { + return; + } + auto outcome = actionsIFace->GetActionStateBoolean(actionHandle); + if (outcome.IsSuccess()) + { + if (outcome.GetValue()) + { + // up + m_movement.SetZ(m_movementSensitivity); + } + } + } + // Pose + { + auto actionHandle = actionsIFace->GetActionHandle("my_action_set", "left_pose"); + if (!actionHandle.IsValid()) + { + return; + } + auto outcome = actionsIFace->GetActionStatePose(actionHandle); + if (outcome.IsSuccess()) + { + AZ::Transform tm(outcome.TakeValue()); + AZ_Printf("Galib", "left_pose tm=\n%s\n", AZStd::to_string(tm).c_str()); + } + } + + } + // Camera::CameraNotificationBus::Handler overrides void XRCameraMovementComponent::OnActiveViewChanged(const AZ::EntityId& activeEntityId) { diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h index 8965cfdd9..963aab5c1 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h @@ -51,6 +51,7 @@ namespace OpenXRVk private: void OnXRControllerEvent(const AzFramework::InputChannel& inputChannel); + void ProcessOpenXRActions(); // Transient data... AZ::Vector3 m_movement = AZ::Vector3::CreateZero(); From bbde2310d9acad9ae65cf75661856e4170e375c7 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 19 Jan 2024 09:14:12 -0600 Subject: [PATCH 10/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRInteractionProfileBus.h | 3 + .../Code/Include/OpenXRVk/OpenXRVkUtils.h | 1 + .../KHRSimpleProfileSystemComponent.cpp | 28 +++++- .../Code/Source/OpenXRActionsManager.cpp | 87 +++++++++++-------- .../Code/Source/OpenXRActionsManager.h | 24 +++-- Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp | 18 ++++ .../Code/Source/XRCameraMovementComponent.cpp | 3 +- 7 files changed, 118 insertions(+), 46 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h index c8c3eba22..f7cbb0c2b 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h @@ -51,6 +51,9 @@ namespace OpenXRVk //! @param componentPath Typically comes from OpenXRActionPath::m_componentPath. virtual ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const = 0; + //! @returns An interaction profile/provider string path that looks like: + //! "/interaction_profiles/khr/simple_controller" + //! "/interaction_profiles/oculus/touch_controller" virtual AZStd::string GetInteractionProviderPath() const = 0; // ///////////////////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h index 0a23f2127..579a7c572 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h @@ -61,6 +61,7 @@ namespace OpenXRVk const char* GetResultString(const XrResult result); void PrintXrError(const char* windowName, const XrResult error, const char* fmt, ...); XR::RawStringList FilterList(const XR::RawStringList& source, const XR::StringList& filter); + AZStd::string ConvertXrPathToString(XrInstance xrInstance, XrPath xrPath); //! Input is an array of chars with multiple ' ' char embedded in it, indicating the start of a new string. //! Iterate through the characters while caching the starting pointer to a string diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp index 4e8343ec1..956d9fc29 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp @@ -29,9 +29,27 @@ namespace OpenXRVk void KHRSimpleProfileSystemComponent::Activate() { - m_name = { - "Khronos Simple", // Khronos Simple Interaction Profile - "/interaction_profiles/khr/simple_controller" + // m_name = { + // "Khronos Simple", // Khronos Simple Interaction Profile + // "/interaction_profiles/khr/simple_controller" + // }; + // + // m_userPaths = { + // {LeftHand, "/user/hand/left"}, + // {RightHand, "/user/hand/right"} + // }; + // + // const AZStd::vector commonPaths = { + // {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + // {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + // {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + // {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + // {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, + // }; + + m_name = { + "Oculus Touch", + "/interaction_profiles/oculus/touch_controller" }; m_userPaths = { @@ -40,7 +58,9 @@ namespace OpenXRVk }; const AZStd::vector commonPaths = { - {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + {{"X Click", "/input/x/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + {{"X Touch", "/input/x/touch"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, // Just Testing. NOT supported in oculus {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index 359a1d9a5..e57bdb4db 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -45,31 +45,44 @@ namespace OpenXRVk return true; } - AZStd::unordered_set activeProfiles; - AZStd::vector activeBindings; + SuggestedBindingsPerProfile suggestedBindingsPerProfile; for (const auto& actionSet : actionsBindingAsset->m_actionSets) { - if (!InitActionSetInternal(actionSet, activeProfiles, activeBindings)) + if (!InitActionSetInternal(actionSet, suggestedBindingsPerProfile)) { return false; } } - if (activeBindings.empty() || activeProfiles.empty()) + if (suggestedBindingsPerProfile.empty()) { AZ_Printf(LogName, "This application will run without actions.\n"); return true; } // Register the bindings for each active interaction profile. - for (const auto& profilePath : activeProfiles) + uint32_t activeProfileCount = 0; + for (const auto& [profileName, suggestedBindings] : suggestedBindingsPerProfile) + { + XrInteractionProfileSuggestedBinding xrSuggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING }; + xrSuggestedBindings.interactionProfile = suggestedBindings.m_profileXrPath; + xrSuggestedBindings.suggestedBindings = suggestedBindings.m_suggestedBindingsList.data(); + xrSuggestedBindings.countSuggestedBindings = static_cast(suggestedBindings.m_suggestedBindingsList.size()); + XrResult result = xrSuggestInteractionProfileBindings(m_xrInstance, &xrSuggestedBindings); + if (IsError(result)) + { + PrintXrError(LogName, result, "Got an error during suggested bindings registration for profile [%s].", profileName.c_str()); + } + else + { + AZ_Printf(LogName, "Successfully registred action bindings for profile [%s].\n", profileName.c_str()); + activeProfileCount++; + } + } + if (activeProfileCount < 1) { - XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING }; - suggestedBindings.interactionProfile = profilePath; - suggestedBindings.suggestedBindings = activeBindings.data(); - suggestedBindings.countSuggestedBindings = static_cast(activeBindings.size()); - XrResult result = xrSuggestInteractionProfileBindings(m_xrInstance, &suggestedBindings); - WARN_IF_UNSUCCESSFUL(result); + AZ_Error(LogName, false, "Failed to activate at least one interaction profile. This application will run without actions.\n"); + return true; } AZStd::vector xrActionSets; @@ -123,8 +136,7 @@ namespace OpenXRVk bool ActionsManager::InitActionSetInternal(const OpenXRActionSet& actionSet, - AZStd::unordered_set& activeProfiles, - AZStd::vector& activeBindings) + SuggestedBindingsPerProfile& suggestedBindingsPerProfile) { // Create an action set. XrActionSetCreateInfo actionSetCreateInfo{}; @@ -153,7 +165,7 @@ namespace OpenXRVk ActionSetInfo& newActionSetInfo = m_actionSets.back(); for (const auto& action : actionSet.m_actions) { - if (!InitActionBindingsInternal(newActionSetInfo, action, activeProfiles, activeBindings)) + if (!InitActionBindingsInternal(newActionSetInfo, action, suggestedBindingsPerProfile)) { AZ_Error(LogName, false, "Failed to created action named [%s] under actionSet named [%s].", action.m_name.c_str(), actionSet.m_name.c_str()); @@ -220,8 +232,7 @@ namespace OpenXRVk uint32_t ActionsManager::AppendActionBindings(const OpenXRAction& action, XrAction newXrAction, - AZStd::unordered_set& activeProfiles, - AZStd::vector& activeBindings) const + SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const { uint32_t additionalBindingsCount = 0; for (const auto& actionPath : action.m_actionPaths) @@ -250,21 +261,28 @@ namespace OpenXRVk continue; } - auto interactionProfilePathStr = interactionProviderIface->GetInteractionProviderPath(); - XrPath xrProviderPath; - result = xrStringToPath(m_xrInstance, interactionProfilePathStr.c_str(), &xrProviderPath); - if (IsError(result)) + // If the interaction profile is not in the dictionary, then add it. + auto profilePathStr = interactionProviderIface->GetInteractionProviderPath(); + if (!suggestedBindingsPerProfile.contains(profilePathStr)) { - PrintXrError(LogName, result, "Failed to create XrPath for action provider [%s], provider path [%s].\n", - actionPath.m_interactionProfile.c_str(), interactionProfilePathStr.c_str()); - continue; + XrPath profileXrPath; + result = xrStringToPath(m_xrInstance, profilePathStr.c_str(), &profileXrPath); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to get profile [%s] XrPath while working on action [%s] and the action path [%s]", + profilePathStr.c_str(), action.m_name.c_str(), actionPath.GetEditorText().c_str()); + continue; + } + suggestedBindingsPerProfile.emplace(profilePathStr, SuggestedBindings{}); + SuggestedBindings& newProfileBindings = suggestedBindingsPerProfile.at(profilePathStr); + newProfileBindings.m_profileXrPath = profileXrPath; } - activeProfiles.emplace(xrProviderPath); + SuggestedBindings& profileBindings = suggestedBindingsPerProfile.at(profilePathStr); XrActionSuggestedBinding binding; binding.action = newXrAction; binding.binding = xrBindingPath; - activeBindings.push_back(binding); + profileBindings.m_suggestedBindingsList.push_back(binding); additionalBindingsCount++; } @@ -272,8 +290,7 @@ namespace OpenXRVk } bool ActionsManager::InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, - AZStd::unordered_set& activeProfiles, - AZStd::vector& activeBindings) + SuggestedBindingsPerProfile& suggestedBindingsPerProfile) { // One OpenXRAction object will become one XrAction. // An OpenXRAction contains a list of OpenXRActionPath that need to be bound. @@ -290,7 +307,7 @@ namespace OpenXRVk } // For each actionPath in the list, create the XrPath and its binding. - const auto additionalBindingsCount = AppendActionBindings(action, newXrAction, activeProfiles, activeBindings); + const auto additionalBindingsCount = AppendActionBindings(action, newXrAction, suggestedBindingsPerProfile); if (additionalBindingsCount < 1) { // This action has no bindings. Don't add it to the active actions list. @@ -347,16 +364,16 @@ namespace OpenXRVk AZ_Printf(LogName, "Got an NULL Interaction Profile for [%s].\n", topPathstr.c_str()); continue; } - constexpr uint32_t numBytes = 256; - char pathAsCStr[numBytes]; - uint32_t validBytes = 0; - result = xrPathToString(m_xrInstance, profileStateOut.interactionProfile, numBytes, &validBytes, pathAsCStr); - if (IsError(result)) + AZStd::string activeProfileName = ConvertXrPathToString(m_xrInstance, profileStateOut.interactionProfile); + if (activeProfileName.empty()) { - PrintXrError(LogName, result, "Failed to convert XrPath to string for user top path [%s]", topPathstr.c_str()); + PrintXrError(LogName, result, "Failed to convert Interaction Profile XrPath to string for user top path [%s]", topPathstr.c_str()); continue; } - AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", topPathstr.c_str(), pathAsCStr); + else + { + AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", topPathstr.c_str(), activeProfileName.c_str()); + } } return true; }); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index f7bedb4a8..0a818ef7d 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -73,10 +73,24 @@ namespace OpenXRVk // Asset Cache relative path static constexpr char DefaultActionsAssetPath[] = "openxr.xractions"; + // The following struct will be used at initialization only. + // All the bindings for all actions will be called + struct SuggestedBindings + { + // Interaction Profile XrPath + XrPath m_profileXrPath; + // List of action bindings that will be registred for this profile. + AZStd::vector m_suggestedBindingsList; + }; + //! The key is the user friendly profile name. + using SuggestedBindingsPerProfile = AZStd::unordered_map; + //! @param actionSet The user configured data for the ActionSet. + //! @param suggestedBindingsPerProfile In this dictionary we will collect all the action bindings + //! for each interaction profile. bool InitActionSetInternal(const OpenXRActionSet& actionSet, - AZStd::unordered_set& activeProfiles, - AZStd::vector& activeBindings); + SuggestedBindingsPerProfile& suggestedBindingsPerProfile + ); struct ActionInfo { @@ -97,15 +111,13 @@ namespace OpenXRVk }; bool InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, - AZStd::unordered_set& activeProfiles, - AZStd::vector& activeBindings); + SuggestedBindingsPerProfile& suggestedBindingsPerProfile); XrAction CreateXrActionAndXrSpace(const ActionSetInfo& actionSetInfo, const OpenXRAction& action, const XrActionType actionType, XrSpace& newXrActionSpace) const; uint32_t AppendActionBindings(const OpenXRAction& action, XrAction newXrAction, - AZStd::unordered_set& activeProfiles, - AZStd::vector& activeBindings) const; + SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const; AZ::Outcome ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false); void RecreateXrActiveActionSets(); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp index a7bca9385..aaae634f1 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp @@ -134,4 +134,22 @@ namespace OpenXRVk return pose; } + AZStd::string ConvertXrPathToString(XrInstance xrInstance, XrPath xrPath) + { + if ((xrInstance == XR_NULL_HANDLE) || (xrPath == XR_NULL_PATH)) + { + return AZStd::string(""); + } + constexpr uint32_t MaxBytes = 256; + char pathAsChars[MaxBytes]; + uint32_t usedBytes = 0; + XrResult result = xrPathToString(xrInstance, xrPath, MaxBytes, &usedBytes, pathAsChars); + if (IsError(result)) + { + PrintXrError("OpenXRVkUtils", result, "Failed to convert xrPath to a string with %u bytes.", MaxBytes); + return AZStd::string(""); + } + return AZStd::string(pathAsChars); + } + } diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp index 91e7775b5..c1672a354 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp @@ -173,6 +173,7 @@ namespace OpenXRVk } // Button { + m_movement.SetZ(0.0f); auto actionHandle = actionsIFace->GetActionHandle("my_action_set", "button"); if (!actionHandle.IsValid()) { @@ -199,7 +200,7 @@ namespace OpenXRVk if (outcome.IsSuccess()) { AZ::Transform tm(outcome.TakeValue()); - AZ_Printf("Galib", "left_pose tm=\n%s\n", AZStd::to_string(tm).c_str()); + //AZ_Printf("Galib", "left_pose tm=\n%s\n", AZStd::to_string(tm).c_str()); } } From d4f248ebcbf46a31a4a4dcfb6f56938863fc0a03 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:20:33 -0600 Subject: [PATCH 11/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRInteractionProfileDescriptor.cpp | 154 ++++++++++++ .../OpenXRInteractionProfileDescriptor.h | 88 +++++++ .../OpenXRInteractionProfilesAsset.cpp | 43 ++++ .../OpenXRInteractionProfilesAsset.h | 42 ++++ .../OpenXRInteractionProfilesAssetHandler.cpp | 223 ++++++++++++++++++ .../OpenXRInteractionProfilesAssetHandler.h | 40 ++++ 6 files changed, 590 insertions(+) create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp new file mode 100644 index 000000000..1b49b2bd1 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "OpenXRInteractionProfileDescriptor.h" + +namespace OpenXRVk +{ + /////////////////////////////////////////////////////////// + /// OpenXRInteractionComponentPathDescriptor + XrActionType OpenXRInteractionComponentPathDescriptor::GetXrActionType(AZStd::string_view actionTypeStr) + { + if (actionTypeStr == s_TypeBoolStr) + { + return XR_ACTION_TYPE_BOOLEAN_INPUT; + } + else if (actionTypeStr == s_TypeFloatStr) + { + return XR_ACTION_TYPE_FLOAT_INPUT; + } + else if (actionTypeStr == s_TypeVector2Str) + { + return XR_ACTION_TYPE_VECTOR2F_INPUT; + } + else if (actionTypeStr == s_TypePoseStr) + { + return XR_ACTION_TYPE_POSE_INPUT; + } + else if (actionTypeStr == s_TypePoseStr) + { + return XR_ACTION_TYPE_VIBRATION_OUTPUT; + } + return XR_ACTION_TYPE_MAX_ENUM; + } + + XrActionType OpenXRInteractionComponentPathDescriptor::GetXrActionType() const + { + return GetXrActionType(m_actionTypeStr); + } + + static AZStd::vector GetEditorXrActionTypeNames() + { + static AZStd::vector s_actionTypeNames = { + {OpenXRInteractionComponentPathDescriptor::s_TypeBoolStr }, + {OpenXRInteractionComponentPathDescriptor::s_TypeFloatStr }, + {OpenXRInteractionComponentPathDescriptor::s_TypeVector2Str}, + {OpenXRInteractionComponentPathDescriptor::s_TypePoseStr }, + {OpenXRInteractionComponentPathDescriptor::s_TypeVibrationStr }, + }; + return s_actionTypeNames; + } + + void OpenXRInteractionComponentPathDescriptor::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRInteractionComponentPathDescriptor::m_name) + ->Field("Path", &OpenXRInteractionComponentPathDescriptor::m_path) + ->Field("ActionType", &OpenXRInteractionComponentPathDescriptor::m_actionTypeStr) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("Component Path", "An OpenXR Component Path that is supported by an OpenXR User Path") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionComponentPathDescriptor::m_name, "Name", "User friendly name.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionComponentPathDescriptor::m_path, "Path", "An OpenXR Path string that starts with '/' BUT is relative to a User Path.") + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRInteractionComponentPathDescriptor::m_actionTypeStr, "Action Type", "Data type of this action.") + ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorXrActionTypeNames) + ; + } + } + } + /// OpenXRInteractionComponentPathDescriptor + /////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////// + /// OpenXRInteractionUserPathDescriptor + void OpenXRInteractionUserPathDescriptor::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRInteractionUserPathDescriptor::m_name) + ->Field("Path", &OpenXRInteractionUserPathDescriptor::m_path) + ->Field("ComponentPaths", &OpenXRInteractionUserPathDescriptor::m_componentPathDescriptors) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("User Path", "Represents a User Path supported by an Interaction Profile") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_name, "Name", "User friendly name.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_path, "Path", "An OpenXR Path string that starts with '/'.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_componentPathDescriptors, "Component Paths", "List of component paths supported by this User Path") + ; + } + } + } + /// OpenXRInteractionUserPathDescriptor + /////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////// + /// OpenXRInteractionProfileDescriptor + void OpenXRInteractionProfileDescriptor::Reflect(AZ::ReflectContext* context) + { + OpenXRInteractionComponentPathDescriptor::Reflect(context); + OpenXRInteractionUserPathDescriptor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("UniqueName", &OpenXRInteractionProfileDescriptor::m_uniqueName) + ->Field("Path", &OpenXRInteractionProfileDescriptor::m_path) + ->Field("UserPathDescriptors", &OpenXRInteractionProfileDescriptor::m_userPathDescriptors) + ->Field("CommonComponentPathDescriptors", &OpenXRInteractionProfileDescriptor::m_commonComponentPathDescriptors) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + "Interaction Profile", "Defines an OpenXR Interaction Profile Supported by O3DE.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Unique Name", "Unique name across all interaction profiles") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Path", "OpenXR Canonical Path for this interation profile.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "User Paths", "List of user paths") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Common Component Paths", "List of component paths supported by all User Paths") + ; + } + } + } + /// OpenXRInteractionProfileDescriptor + /////////////////////////////////////////////////////////// + +} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h new file mode 100644 index 000000000..54ac653da --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace OpenXRVk +{ + class OpenXRInteractionComponentPathDescriptor final + { + public: + AZ_RTTI(OpenXRInteractionComponentPathDescriptor, "{E2038854-929D-484F-A34E-1C7390EE2CCB}"); + virtual ~OpenXRInteractionComponentPathDescriptor() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + static constexpr AZStd::string_view s_TypeBoolStr{ "XrBool" }; + static constexpr AZStd::string_view s_TypeFloatStr = "XrFloat"; + static constexpr AZStd::string_view s_TypeVector2Str = "XrVector2"; + static constexpr AZStd::string_view s_TypePoseStr = "XrPose"; + static constexpr AZStd::string_view s_TypeVibrationStr = "XrVibration"; + + //! Helper method + static XrActionType GetXrActionType(AZStd::string_view actionTypeStr); + XrActionType GetXrActionType() const; + + //! A user friendly name. + AZStd::string m_name; + //! For OpenXR a Component Path string would look like: + //! "/input/x/click", or "/input/trigger/value", etc + AZStd::string m_path; + //! Whether this is a boolean, float, vector2 or pose. + //! The user will be presented with a combo box to avoid + //! chances for error. + AZStd::string m_actionTypeStr; + }; + + class OpenXRInteractionUserPathDescriptor final + { + public: + AZ_RTTI(OpenXRInteractionUserPathDescriptor, "{F3913A15-41FC-4EC9-A381-296C0AB6D6C6}"); + virtual ~OpenXRInteractionUserPathDescriptor() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + //! A user friendly name. + AZStd::string m_name; + //! For OpenXR a User Path string would look like: + //! "/user/hand/left", or "/user/hand/right", etc + AZStd::string m_path; + //! Component Paths that are only supported under this user path. + //! This list can be empty. + AZStd::vector m_componentPathDescriptors; + }; + + class OpenXRInteractionProfileDescriptor final + { + public: + AZ_RTTI(OpenXRInteractionProfileDescriptor, "{BC73B4BC-4F15-4B1E-AEA9-B133FBB5AD16}"); + virtual ~OpenXRInteractionProfileDescriptor() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + //! Unique name across all OpenXRInteractionProfileDescriptor. + //! It serves also as user friendly display name, and because + //! it is unique it can be used in a dictionary. + AZStd::string m_uniqueName; + AZStd::string m_path; + + AZStd::vector m_userPathDescriptors; + // ComponentsPaths that are supported by all User Paths listed in @m_userPathDescriptors + AZStd::vector m_commonComponentPathDescriptors; + }; + +}// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp new file mode 100644 index 000000000..2e1368d6a --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "OpenXRInteractionProfilesAsset.h" + +namespace OpenXRVk +{ + /////////////////////////////////////////////////////////// + /// OpenXRActionBindingsAsset + void OpenXRInteractionProfilesAsset::Reflect(AZ::ReflectContext* context) + { + OpenXRInteractionProfileDescriptor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) + ->Field("InteractionProfiles", &OpenXRInteractionProfilesAsset::m_interactionProfileDescriptors) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + s_assetTypeName, "Defines the OpenXR Interaction Profiles supported by O3DE.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfilesAsset::m_interactionProfileDescriptors, "Interaction Profiles", "List of interaction profile descriptors.") + ; + } + } + } + /// OpenXRActionBindingsAsset + /////////////////////////////////////////////////////////// + +} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h new file mode 100644 index 000000000..5df9e8f78 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "InteractionProfiles/OpenXRInteractionProfileDescriptor.h" + + +namespace OpenXRVk +{ + //! This asset defines a list of Interaction Profile Descriptors. + //! The Engine only needs one of these assets, which is used to express + //! all the different interaction profiles (aka XR Headset Devices) that + //! are supported by OpenXR. + //! Basically this asset contains data as listed here: + //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles + class OpenXRInteractionProfilesAsset final + : public AZ::Data::AssetData + { + public: + AZ_RTTI(OpenXRActionBindingsAsset, "{02555DCD-E363-42FB-935C-4E67CC3A1699}", AZ::Data::AssetData); + AZ_CLASS_ALLOCATOR(OpenXRActionBindingsAsset, AZ::SystemAllocator); + static void Reflect(AZ::ReflectContext* context); + + static constexpr char s_assetTypeName[] = "OpenXR Interaction Profiles"; + static constexpr char s_assetExtension[] = "xrprofiles"; + + AZStd::vector m_interactionProfileDescriptors; + }; +}// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp new file mode 100644 index 000000000..350a281e4 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include "OpenXRActionsBindingAsset.h" + +namespace OpenXRVk +{ + /////////////////////////////////////////////////////////// + /// OpenXRActionPath + void OpenXRActionPath::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Profile", &OpenXRActionPath::m_interactionProfile) + ->Field("UserPath", &OpenXRActionPath::m_userPath) + ->Field("ComponentPath", &OpenXRActionPath::m_componentPath) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRActionPath", "A specific OpenXR I/O action path.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionPath::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_interactionProfile, "Interaction Profile", "The Interaction I/O profile") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnInteractionProfileSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetInteractionProfiles) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_userPath, "User Path", "Root path identifier") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnUserPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetUserPaths) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_componentPath, "I/O Component Path", "I/O Component Path identifier") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnComponentPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetComponentPaths) + ; + } + } + } + + AZStd::string OpenXRActionPath::GetEditorText() const + { + return AZStd::string::format("%s %s %s", m_interactionProfile.c_str(), m_userPath.c_str(), m_componentPath.c_str()); + } + + AZ::Crc32 OpenXRActionPath::OnInteractionProfileSelected() + { + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } + + AZStd::vector OpenXRActionPath::GetInteractionProfiles() const + { + AZStd::vector retList; + + OpenXRInteractionProfileBus::EnumerateHandlers([&retList](OpenXRInteractionProfile* handler) -> bool { + retList.push_back(handler->GetName()); + return true; + }); + + return retList; + } + + AZ::Crc32 OpenXRActionPath::OnUserPathSelected() + { + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } + + AZStd::vector OpenXRActionPath::GetUserPaths() const + { + AZStd::vector retList; + + OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetUserPaths); + + return retList; + } + + AZ::Crc32 OpenXRActionPath::OnComponentPathSelected() + { + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } + + AZStd::vector OpenXRActionPath::GetComponentPaths() const + { + AZStd::vector retList; + + OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetComponentPaths, m_userPath); + + return retList; + } + /// OpenXRActionPath + /////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////// + /// OpenXRAction + void OpenXRAction::Reflect(AZ::ReflectContext* context) + { + OpenXRActionPath::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRAction::m_name) + ->Field("LocalizedName", &OpenXRAction::m_localizedName) + ->Field("ActionPaths", &OpenXRAction::m_actionPaths) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRAction", "An action bound to one or more OpenXR I/O Paths.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRAction::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_name, "Name", "Runtime identifier for this action.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_localizedName, "Localized Name", "User friendly display name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_actionPaths, "Action Paths", "List of action paths bound to this action") + ; + } + } + } + + AZStd::string OpenXRAction::GetEditorText() const + { + if (!m_localizedName.empty()) + { + return m_localizedName; + } + return m_name.empty() ? "" : m_name; + } + /// OpenXRAction + /////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////// + /// OpenXRActionSet + void OpenXRActionSet::Reflect(AZ::ReflectContext* context) + { + OpenXRAction::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRActionSet::m_name) + ->Field("LocalizedName", &OpenXRActionSet::m_localizedName) + ->Field("Priority", &OpenXRActionSet::m_priority) + ->Field("Actions", &OpenXRActionSet::m_actions) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRActionSet", "A group of OpenXR Actions that can be selectively activated/deactivated at runtime.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionSet::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_name, "Name", "Runtime identifier for this action set.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_localizedName, "Localized Name", "Action set display name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSet::m_priority, "Priority", "The higher this value the higher the priority.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_actions, "Actions", "List of actions for this action set.") + ; + } + } + } + + AZStd::string OpenXRActionSet::GetEditorText() const + { + if (!m_localizedName.empty()) + { + return m_localizedName; + } + return m_name.empty() ? "" : m_name; + } + /// OpenXRActionSet + /////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////// + /// OpenXRActionBindingsAsset + void OpenXRActionBindingsAsset::Reflect(AZ::ReflectContext* context) + { + OpenXRActionSet::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) + ->Field("ActionSets", &OpenXRActionBindingsAsset::m_actionSets) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + s_assetTypeName, "Defines the OpenXR Actions an application cares about.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionBindingsAsset::m_actionSets, "Action Sets", "List of action sets.") + ; + } + } + } + /// OpenXRActionBindingsAsset + /////////////////////////////////////////////////////////// + +} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h new file mode 100644 index 000000000..d053140a1 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +#include "OpenXRInteractionProfilesAsset.h" + + +namespace OpenXRVk +{ + //! This asset defines a list of Interaction Profile Descriptors. + //! The Engine only needs one of these assets, which is used to express + //! all the different interaction profiles (aka XR Headset Devices) that + //! are supported by OpenXR. + //! Basically this asset contains data as listed here: + //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles + class OpenXRInteractionProfilesAssetHandler final + : public AzFramework::GenericAssetHandler + { + public: + AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); + AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); + static void Reflect(AZ::ReflectContext* context); + + }; +}// namespace OpenXRVk From 5174e422ece0475a11525c778b8c601097b83878 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 19 Jan 2024 16:17:48 -0600 Subject: [PATCH 12/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkSystemComponent.h | 2 + .../OpenXRInteractionProfileDescriptor.cpp | 6 +- .../OpenXRInteractionProfilesAsset.cpp | 12 +- .../OpenXRInteractionProfilesAsset.h | 19 +- .../OpenXRInteractionProfilesAssetHandler.cpp | 223 ------------------ .../OpenXRInteractionProfilesAssetHandler.h | 40 ---- .../Code/Source/OpenXRVkSystemComponent.cpp | 5 + .../Code/openxrvk_private_common_files.cmake | 8 +- 8 files changed, 43 insertions(+), 272 deletions(-) delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h index b7dfa5491..1b0764f79 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h @@ -13,6 +13,7 @@ #include #include #include "OpenXRActionsBindingAsset.h" +#include "InteractionProfiles/OpenXRInteractionProfilesAsset.h" namespace OpenXRVk { @@ -66,5 +67,6 @@ namespace OpenXRVk private: XR::Ptr m_instance; AZStd::unique_ptr> m_actionsBindingAssetHandler; + AZStd::unique_ptr m_interactionProfilesAssetHandler; }; } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp index 1b49b2bd1..6ed691566 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp @@ -141,9 +141,9 @@ namespace OpenXRVk ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Unique Name", "Unique name across all interaction profiles") - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Path", "OpenXR Canonical Path for this interation profile.") - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "User Paths", "List of user paths") - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Common Component Paths", "List of component paths supported by all User Paths") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_path, "Path", "OpenXR Canonical Path for this interation profile.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_userPathDescriptors, "User Paths", "List of user paths") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_commonComponentPathDescriptors, "Common Component Paths", "List of component paths supported by all User Paths") ; } } diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp index 2e1368d6a..83767bfa1 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp @@ -11,7 +11,7 @@ namespace OpenXRVk { /////////////////////////////////////////////////////////// - /// OpenXRActionBindingsAsset + /// OpenXRInteractionProfilesAsset void OpenXRInteractionProfilesAsset::Reflect(AZ::ReflectContext* context) { OpenXRInteractionProfileDescriptor::Reflect(context); @@ -37,7 +37,15 @@ namespace OpenXRVk } } } - /// OpenXRActionBindingsAsset + /// OpenXRInteractionProfilesAsset /////////////////////////////////////////////////////////// + OpenXRInteractionProfilesAssetHandler::OpenXRInteractionProfilesAssetHandler() + : AzFramework::GenericAssetHandler( + OpenXRInteractionProfilesAsset::s_assetTypeName, + "Other", + OpenXRInteractionProfilesAsset::s_assetExtension) + { + } + } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h index 5df9e8f78..475f540c3 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h @@ -15,6 +15,8 @@ #include #include +#include + #include "InteractionProfiles/OpenXRInteractionProfileDescriptor.h" @@ -30,8 +32,8 @@ namespace OpenXRVk : public AZ::Data::AssetData { public: - AZ_RTTI(OpenXRActionBindingsAsset, "{02555DCD-E363-42FB-935C-4E67CC3A1699}", AZ::Data::AssetData); - AZ_CLASS_ALLOCATOR(OpenXRActionBindingsAsset, AZ::SystemAllocator); + AZ_RTTI(OpenXRInteractionProfilesAsset, "{02555DCD-E363-42FB-935C-4E67CC3A1699}", AZ::Data::AssetData); + AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAsset, AZ::SystemAllocator); static void Reflect(AZ::ReflectContext* context); static constexpr char s_assetTypeName[] = "OpenXR Interaction Profiles"; @@ -39,4 +41,17 @@ namespace OpenXRVk AZStd::vector m_interactionProfileDescriptors; }; + + //! Custom asset handler + class OpenXRInteractionProfilesAssetHandler final + : public AzFramework::GenericAssetHandler + { + public: + AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); + AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); + + OpenXRInteractionProfilesAssetHandler(); + }; + + }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp deleted file mode 100644 index 350a281e4..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include -#include "OpenXRActionsBindingAsset.h" - -namespace OpenXRVk -{ - /////////////////////////////////////////////////////////// - /// OpenXRActionPath - void OpenXRActionPath::Reflect(AZ::ReflectContext* context) - { - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) - { - serialize->Class() - ->Version(1) - ->Field("Profile", &OpenXRActionPath::m_interactionProfile) - ->Field("UserPath", &OpenXRActionPath::m_userPath) - ->Field("ComponentPath", &OpenXRActionPath::m_componentPath) - ; - - AZ::EditContext* edit = serialize->GetEditContext(); - if (edit) - { - edit->Class("OpenXRActionPath", "A specific OpenXR I/O action path.") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionPath::GetEditorText) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_interactionProfile, "Interaction Profile", "The Interaction I/O profile") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnInteractionProfileSelected) - ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetInteractionProfiles) - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_userPath, "User Path", "Root path identifier") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnUserPathSelected) - ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetUserPaths) - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_componentPath, "I/O Component Path", "I/O Component Path identifier") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnComponentPathSelected) - ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetComponentPaths) - ; - } - } - } - - AZStd::string OpenXRActionPath::GetEditorText() const - { - return AZStd::string::format("%s %s %s", m_interactionProfile.c_str(), m_userPath.c_str(), m_componentPath.c_str()); - } - - AZ::Crc32 OpenXRActionPath::OnInteractionProfileSelected() - { - return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; - } - - AZStd::vector OpenXRActionPath::GetInteractionProfiles() const - { - AZStd::vector retList; - - OpenXRInteractionProfileBus::EnumerateHandlers([&retList](OpenXRInteractionProfile* handler) -> bool { - retList.push_back(handler->GetName()); - return true; - }); - - return retList; - } - - AZ::Crc32 OpenXRActionPath::OnUserPathSelected() - { - return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; - } - - AZStd::vector OpenXRActionPath::GetUserPaths() const - { - AZStd::vector retList; - - OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetUserPaths); - - return retList; - } - - AZ::Crc32 OpenXRActionPath::OnComponentPathSelected() - { - return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; - } - - AZStd::vector OpenXRActionPath::GetComponentPaths() const - { - AZStd::vector retList; - - OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetComponentPaths, m_userPath); - - return retList; - } - /// OpenXRActionPath - /////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////// - /// OpenXRAction - void OpenXRAction::Reflect(AZ::ReflectContext* context) - { - OpenXRActionPath::Reflect(context); - - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) - { - serialize->Class() - ->Version(1) - ->Field("Name", &OpenXRAction::m_name) - ->Field("LocalizedName", &OpenXRAction::m_localizedName) - ->Field("ActionPaths", &OpenXRAction::m_actionPaths) - ; - - AZ::EditContext* edit = serialize->GetEditContext(); - if (edit) - { - edit->Class("OpenXRAction", "An action bound to one or more OpenXR I/O Paths.") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRAction::GetEditorText) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_name, "Name", "Runtime identifier for this action.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_localizedName, "Localized Name", "User friendly display name.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_actionPaths, "Action Paths", "List of action paths bound to this action") - ; - } - } - } - - AZStd::string OpenXRAction::GetEditorText() const - { - if (!m_localizedName.empty()) - { - return m_localizedName; - } - return m_name.empty() ? "" : m_name; - } - /// OpenXRAction - /////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////// - /// OpenXRActionSet - void OpenXRActionSet::Reflect(AZ::ReflectContext* context) - { - OpenXRAction::Reflect(context); - - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) - { - serialize->Class() - ->Version(1) - ->Field("Name", &OpenXRActionSet::m_name) - ->Field("LocalizedName", &OpenXRActionSet::m_localizedName) - ->Field("Priority", &OpenXRActionSet::m_priority) - ->Field("Actions", &OpenXRActionSet::m_actions) - ; - - AZ::EditContext* edit = serialize->GetEditContext(); - if (edit) - { - edit->Class("OpenXRActionSet", "A group of OpenXR Actions that can be selectively activated/deactivated at runtime.") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionSet::GetEditorText) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_name, "Name", "Runtime identifier for this action set.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_localizedName, "Localized Name", "Action set display name.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSet::m_priority, "Priority", "The higher this value the higher the priority.") - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_actions, "Actions", "List of actions for this action set.") - ; - } - } - } - - AZStd::string OpenXRActionSet::GetEditorText() const - { - if (!m_localizedName.empty()) - { - return m_localizedName; - } - return m_name.empty() ? "" : m_name; - } - /// OpenXRActionSet - /////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////// - /// OpenXRActionBindingsAsset - void OpenXRActionBindingsAsset::Reflect(AZ::ReflectContext* context) - { - OpenXRActionSet::Reflect(context); - - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) - { - serialize->Class() - ->Version(1) - ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) - ->Field("ActionSets", &OpenXRActionBindingsAsset::m_actionSets) - ; - - AZ::EditContext* edit = serialize->GetEditContext(); - if (edit) - { - edit->Class( - s_assetTypeName, "Defines the OpenXR Actions an application cares about.") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionBindingsAsset::m_actionSets, "Action Sets", "List of action sets.") - ; - } - } - } - /// OpenXRActionBindingsAsset - /////////////////////////////////////////////////////////// - -} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h deleted file mode 100644 index d053140a1..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAssetHandler.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -#include "OpenXRInteractionProfilesAsset.h" - - -namespace OpenXRVk -{ - //! This asset defines a list of Interaction Profile Descriptors. - //! The Engine only needs one of these assets, which is used to express - //! all the different interaction profiles (aka XR Headset Devices) that - //! are supported by OpenXR. - //! Basically this asset contains data as listed here: - //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles - class OpenXRInteractionProfilesAssetHandler final - : public AzFramework::GenericAssetHandler - { - public: - AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); - AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); - static void Reflect(AZ::ReflectContext* context); - - }; -}// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index 3846f1d52..f5f98d530 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -39,6 +39,7 @@ namespace OpenXRVk OpenXRActionBindingsAsset::Reflect(context); AzFramework::InputDeviceXRController::Reflect(context); + OpenXRInteractionProfilesAsset::Reflect(context); } XR::Ptr SystemComponent::CreateInstance() @@ -86,6 +87,9 @@ namespace OpenXRVk m_actionsBindingAssetHandler = AZStd::make_unique>(OpenXRActionBindingsAsset::s_assetTypeName, "Other", OpenXRActionBindingsAsset::s_assetExtension); m_actionsBindingAssetHandler->Register(); + m_interactionProfilesAssetHandler = AZStd::make_unique(); + m_interactionProfilesAssetHandler->Register(); + if (XR::IsOpenXREnabled()) { m_instance = AZStd::static_pointer_cast(CreateInstance()); @@ -116,6 +120,7 @@ namespace OpenXRVk } m_actionsBindingAssetHandler->Unregister(); + m_interactionProfilesAssetHandler->Unregister(); } // /////////////////////////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index 068271c83..b4b035f4b 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -31,12 +31,16 @@ set(FILES Source/OpenXRVkSwapChain.cpp Source/OpenXRVkSystemComponent.cpp Source/OpenXRVkUtils.cpp + Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp + Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h + Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp + Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h + Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp + Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h Source/OpenXRActionsBindingAsset.cpp Source/OpenXRActionsBindingAsset.h Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h - Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp - Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h Source/OpenXRActionsManager.cpp Source/OpenXRActionsManager.h Source/OpenXRVisualizedSpacesManager.cpp From 2482a97f7a6fa8b9235b8363d127e1f6cbeecba5 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sat, 20 Jan 2024 07:42:38 -0600 Subject: [PATCH 13/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRInteractionProfileBus.h | 63 ----------------- .../OpenXRInteractionProfileDescriptor.cpp | 65 +++++++++++++++++- .../OpenXRInteractionProfileDescriptor.h | 18 +++++ .../OpenXRInteractionProfilesAsset.cpp | 32 +++++++++ .../OpenXRInteractionProfilesAsset.h | 4 ++ ...enXRInteractionProfilesProviderInterface.h | 68 +++++++++++++++++++ .../Code/Source/OpenXRActionsBindingAsset.cpp | 58 ++++++++++++---- .../Code/Source/OpenXRVkSystemComponent.cpp | 22 ------ .../Code/openxrvk_private_common_files.cmake | 2 +- 9 files changed, 232 insertions(+), 100 deletions(-) delete mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h deleted file mode 100644 index f7cbb0c2b..000000000 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileBus.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -#include -#include -#include - -namespace OpenXRVk -{ - class OpenXRInteractionProfile - : public AZ::EBusTraits - { - public: - static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; - static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById; - // The bus ID is OpenXRActionPath::m_interactionProfile. - // Should be the exact same string as returned by GetName() - typedef AZStd::string BusIdType; - - ////////////////////////////////////////////////////////// - // The following functions are called during asset creation time. - virtual AZStd::string GetName() const = 0; - virtual AZStd::vector GetUserPaths() const = 0; - virtual AZStd::string GetUserTopPath(const AZStd::string& userPathName) const = 0; - virtual AZStd::vector GetComponentPaths(const AZStd::string& userPath) const = 0; - // - ////////////////////////////////////////////////////////// - - - //////////////////////////////////////////////////////////// - // The following functions are called at runtime. - // - struct ActionPathInfo - { - //! Absolute path looks like: - //! /user/hand/left/input/select/click - AZStd::string m_absolutePath; - XrActionType m_actionType = XrActionType::XR_ACTION_TYPE_MAX_ENUM; - }; - - //! @param userPath Typically comes from OpenXRActionPath::m_userPath. - //! @param componentPath Typically comes from OpenXRActionPath::m_componentPath. - virtual ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const = 0; - - //! @returns An interaction profile/provider string path that looks like: - //! "/interaction_profiles/khr/simple_controller" - //! "/interaction_profiles/oculus/touch_controller" - virtual AZStd::string GetInteractionProviderPath() const = 0; - // - ///////////////////////////////////////////////////////////// - }; - - using OpenXRInteractionProfileBus = AZ::EBus; -} diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp index 6ed691566..c9587784f 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp @@ -6,6 +6,8 @@ * */ +#include + #include "OpenXRInteractionProfileDescriptor.h" namespace OpenXRVk @@ -71,8 +73,10 @@ namespace OpenXRVk { edit->Class("Component Path", "An OpenXR Component Path that is supported by an OpenXR User Path") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionComponentPathDescriptor::GetEditorText) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionComponentPathDescriptor::m_name, "Name", "User friendly name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionComponentPathDescriptor::m_path, "Path", "An OpenXR Path string that starts with '/' BUT is relative to a User Path.") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRInteractionComponentPathDescriptor::m_actionTypeStr, "Action Type", "Data type of this action.") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorXrActionTypeNames) @@ -80,6 +84,12 @@ namespace OpenXRVk } } } + + AZStd::string OpenXRInteractionComponentPathDescriptor::GetEditorText() + { + return m_name.empty() ? "" : m_name; + } + /// OpenXRInteractionComponentPathDescriptor /////////////////////////////////////////////////////////// @@ -103,14 +113,22 @@ namespace OpenXRVk { edit->Class("User Path", "Represents a User Path supported by an Interaction Profile") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionUserPathDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_name, "Name", "User friendly name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_path, "Path", "An OpenXR Path string that starts with '/'.") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_componentPathDescriptors, "Component Paths", "List of component paths supported by this User Path") ; } } } + + AZStd::string OpenXRInteractionUserPathDescriptor::GetEditorText() + { + return m_name.empty() ? "" : m_name; + } + /// OpenXRInteractionUserPathDescriptor /////////////////////////////////////////////////////////// @@ -139,8 +157,10 @@ namespace OpenXRVk edit->Class( "Interaction Profile", "Defines an OpenXR Interaction Profile Supported by O3DE.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionProfileDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Unique Name", "Unique name across all interaction profiles") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_path, "Path", "OpenXR Canonical Path for this interation profile.") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_userPathDescriptors, "User Paths", "List of user paths") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_commonComponentPathDescriptors, "Common Component Paths", "List of component paths supported by all User Paths") @@ -148,6 +168,47 @@ namespace OpenXRVk } } } + + AZStd::string OpenXRInteractionProfileDescriptor::GetEditorText() + { + return m_uniqueName.empty() ? "" : m_uniqueName; + } + + AZ::Outcome OpenXRInteractionProfileDescriptor::Validate() const + { + if (m_uniqueName.empty()) + { + return AZ::Failure("OpenXRInteractionProfileDescriptor has no name."); + } + //Spaces at the beginning and end of the name are not allowed. + AZStd::string tmpName(m_uniqueName); + AZ::StringFunc::TrimWhiteSpace(tmpName, true, true); + if (tmpName.empty()) + { + return AZ::Failure("The OpenXRInteractionProfileDescriptor name is just a bunch of spaces."); + } + if (tmpName.size() != m_uniqueName.size()) + { + return AZ::Failure( + AZStd::string::format("Trailing or leading spaces are not allowed in the name of a OpenXRInteractionProfileDescriptor [%s].", + m_uniqueName.c_str()) + ); + } + //TODO: Validate the remaining data. + return AZ::Success(); + } + + const OpenXRInteractionUserPathDescriptor* OpenXRInteractionProfileDescriptor::GetUserPathDescriptor(const AZStd::string& userPathName) const + { + for (const auto& userPathDescriptor : m_userPathDescriptors) + { + if (userPathDescriptor.m_name == userPathName) + { + return &userPathDescriptor; + } + } + return nullptr; + } /// OpenXRInteractionProfileDescriptor /////////////////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h index 54ac653da..2360a7112 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h @@ -46,6 +46,10 @@ namespace OpenXRVk //! The user will be presented with a combo box to avoid //! chances for error. AZStd::string m_actionTypeStr; + + private: + AZStd::string GetEditorText(); + }; class OpenXRInteractionUserPathDescriptor final @@ -64,6 +68,10 @@ namespace OpenXRVk //! Component Paths that are only supported under this user path. //! This list can be empty. AZStd::vector m_componentPathDescriptors; + + private: + AZStd::string GetEditorText(); + }; class OpenXRInteractionProfileDescriptor final @@ -74,6 +82,13 @@ namespace OpenXRVk static void Reflect(AZ::ReflectContext* reflection); + static constexpr char LogName[] = "OpenXRInteractionProfileDescriptor"; + + //! Returns success only if the data makes sense, has proper casing, etc. + AZ::Outcome Validate() const; + + const OpenXRInteractionUserPathDescriptor* GetUserPathDescriptor(const AZStd::string& userPathName) const; + //! Unique name across all OpenXRInteractionProfileDescriptor. //! It serves also as user friendly display name, and because //! it is unique it can be used in a dictionary. @@ -83,6 +98,9 @@ namespace OpenXRVk AZStd::vector m_userPathDescriptors; // ComponentsPaths that are supported by all User Paths listed in @m_userPathDescriptors AZStd::vector m_commonComponentPathDescriptors; + + private: + AZStd::string GetEditorText(); }; }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp index 83767bfa1..35a717ee2 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp @@ -48,4 +48,36 @@ namespace OpenXRVk { } + bool OpenXRInteractionProfilesAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) + { + auto profileAsset = asset.GetAs(); + if (!profileAsset) + { + AZ_Error(LogName, false, "This should be an OpenXR Interaction Profile Asset, as this is the only type this handler can process."); + return false; + } + const auto& descriptorsList = profileAsset->m_interactionProfileDescriptors; + if (descriptorsList.empty()) + { + AZ_Error(LogName, false, "The list of Interaction Profile Descriptors is empty."); + return false; + } + + if (!m_serializeContext) + { + AZ_Error(LogName, false, "Can't save the OpenXR Interaction Profile Asset without a serialize context."); + return false; + } + + for (const auto& profileDescriptor : descriptorsList) + { + if (!profileDescriptor.Validate()) + { + return false; + } + } + return AZ::Utils::SaveObjectToStream(*stream, AZ::ObjectStream::ST_JSON, profileAsset, + asset->RTTI_GetType(), m_serializeContext); + } + } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h index 475f540c3..3d548e9b2 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h @@ -50,7 +50,11 @@ namespace OpenXRVk AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); + static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; + OpenXRInteractionProfilesAssetHandler(); + + bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; }; diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h new file mode 100644 index 000000000..060b342fa --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include +#include + +#include "OpenXRInteractionProfileDescriptor.h" + +namespace OpenXRVk +{ + class IOpenXRInteractionProfilesProvider + { + public: + AZ_RTTI(IOpenXRInteractionProfilesProvider, "{25598F43-3634-4B2D-9A87-95E8BFDAB673}"); + AZ_DISABLE_COPY_MOVE(IOpenXRInteractionProfilesProvider); + + IOpenXRInteractionProfilesProvider() = default; + virtual ~IOpenXRInteractionProfilesProvider() = default; + + virtual const AZStd::vector& GetInteractionProfileNames() const = 0; + + virtual const OpenXRInteractionProfileDescriptor * GetInteractionProfileDescriptor(const AZStd::string profileName) const = 0; + + // ////////////////////////////////////////////////////////// + // // The following functions are called during asset creation time. + // virtual AZStd::string GetName() const = 0; + // virtual AZStd::vector GetUserPaths() const = 0; + // virtual AZStd::string GetUserTopPath(const AZStd::string& userPathName) const = 0; + // virtual AZStd::vector GetComponentPaths(const AZStd::string& userPath) const = 0; + // // + // ////////////////////////////////////////////////////////// + // + // + // //////////////////////////////////////////////////////////// + // // The following functions are called at runtime. + // // + // struct ActionPathInfo + // { + // //! Absolute path looks like: + // //! /user/hand/left/input/select/click + // AZStd::string m_absolutePath; + // XrActionType m_actionType = XrActionType::XR_ACTION_TYPE_MAX_ENUM; + // }; + // + // //! @param userPath Typically comes from OpenXRActionPath::m_userPath. + // //! @param componentPath Typically comes from OpenXRActionPath::m_componentPath. + // virtual ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const = 0; + // + // //! @returns An interaction profile/provider string path that looks like: + // //! "/interaction_profiles/khr/simple_controller" + // //! "/interaction_profiles/oculus/touch_controller" + // virtual AZStd::string GetInteractionProviderPath() const = 0; + // // + // ///////////////////////////////////////////////////////////// + }; + + using OpenXRInteractionProfilesProviderInterface = AZ::Interface; +} diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp index 350a281e4..ff7c6b90f 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp @@ -6,7 +6,7 @@ * */ -#include +#include "InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h" #include "OpenXRActionsBindingAsset.h" namespace OpenXRVk @@ -58,14 +58,12 @@ namespace OpenXRVk AZStd::vector OpenXRActionPath::GetInteractionProfiles() const { - AZStd::vector retList; - - OpenXRInteractionProfileBus::EnumerateHandlers([&retList](OpenXRInteractionProfile* handler) -> bool { - retList.push_back(handler->GetName()); - return true; - }); - - return retList; + auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); + if (!interactionProviderIface) + { + return {}; + } + return interactionProviderIface->GetInteractionProfileNames(); } AZ::Crc32 OpenXRActionPath::OnUserPathSelected() @@ -76,9 +74,21 @@ namespace OpenXRVk AZStd::vector OpenXRActionPath::GetUserPaths() const { AZStd::vector retList; + auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); + if (!interactionProviderIface) + { + return retList; + } - OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetUserPaths); - + const auto * profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfile); + if (!profileDescriptor) + { + return retList; + } + for (const auto& userPathDescriptor : profileDescriptor->m_userPathDescriptors) + { + retList.push_back(userPathDescriptor.m_name); + } return retList; } @@ -90,8 +100,32 @@ namespace OpenXRVk AZStd::vector OpenXRActionPath::GetComponentPaths() const { AZStd::vector retList; + auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); + if (!interactionProviderIface) + { + return retList; + } + + const auto* profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfile); + if (!profileDescriptor) + { + return retList; + } + const auto* userPathDescriptor = profileDescriptor->GetUserPathDescriptor(m_userPath); + if (!userPathDescriptor) + { + return retList; + } + + for (const auto& componentPath : userPathDescriptor->m_componentPathDescriptors) + { + retList.push_back(componentPath.m_name); + } - OpenXRInteractionProfileBus::EventResult(retList, m_interactionProfile, &OpenXRInteractionProfile::GetComponentPaths, m_userPath); + for (const auto& componentPath : profileDescriptor->m_commonComponentPathDescriptors) + { + retList.push_back(componentPath.m_name); + } return retList; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index f5f98d530..f41ead4dd 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -123,26 +123,4 @@ namespace OpenXRVk m_interactionProfilesAssetHandler->Unregister(); } - // /////////////////////////////////////////////////////////////////// - // // AssetTypeInfoBus overrides - // AZ::Data::AssetType SystemComponent::GetAssetType() const - // { - // return AZ::AzTypeInfo::Uuid(); - // } - // - // const char* SystemComponent::GetAssetTypeDisplayName() const - // { - // return OpenXRActionBindingsAsset::s_assetTypeName; - // } - // - // void SystemComponent::GetAssetTypeExtensions(AZStd::vector& extensions) - // { - // extensions.push_back(OpenXRActionBindingsAsset::s_assetExtension); - // } - // - // bool SystemComponent::CanCreateComponent([[maybe_unused]] const AZ::Data::AssetId& assetId) const - // { - // return false; - // } - // /////////////////////////////////////////////////////////////////// } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index b4b035f4b..2f491f28d 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -17,7 +17,6 @@ set(FILES Include/OpenXRVk/OpenXRVkSwapChain.h Include/OpenXRVk/OpenXRVkSystemComponent.h Include/OpenXRVk/OpenXRVkUtils.h - Include/OpenXRVk/OpenXRInteractionProfileBus.h Include/OpenXRVk/OpenXRActionsInterface.h Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h Source/InputDeviceXRController.cpp @@ -37,6 +36,7 @@ set(FILES Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h + Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h Source/OpenXRActionsBindingAsset.cpp Source/OpenXRActionsBindingAsset.h Source/XRCameraMovementComponent.cpp From 442f7c1595e2ff7b87bfecc961bcfceb1119fe44 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:20:40 -0600 Subject: [PATCH 14/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- Gems/OpenXRVk/Code/CMakeLists.txt | 63 ++++++- .../Builders/OpenXRActionSetsAssetBuilder.cpp | 41 +++++ .../Builders/OpenXRActionSetsAssetBuilder.h | 39 ++++ .../OpenXRAssetBuildersSystemComponent.cpp | 72 ++++++++ .../OpenXRAssetBuildersSystemComponent.h | 49 +++++ .../Source/Builders/OpenXRBuilderModule.cpp | 47 +++++ .../KHRSimpleProfileSystemComponent.cpp | 167 ------------------ .../KHRSimpleProfileSystemComponent.h | 79 --------- ...sset.cpp => OpenXRActionSetDescriptor.cpp} | 155 +++++++--------- ...ingAsset.h => OpenXRActionSetDescriptor.h} | 43 ++--- .../Code/Source/OpenXRActionSetsAsset.cpp | 47 +++++ .../Code/Source/OpenXRActionSetsAsset.h | 34 ++++ .../Code/Source/OpenXRActionsManager.cpp | 2 +- .../Code/openxrvk_private_builder_files.cmake | 14 ++ .../Code/openxrvk_private_common_files.cmake | 10 +- .../Code/openxrvk_shared_builder_files.cmake | 11 ++ 16 files changed, 494 insertions(+), 379 deletions(-) create mode 100644 Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp create mode 100644 Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h create mode 100644 Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp create mode 100644 Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.h create mode 100644 Gems/OpenXRVk/Code/Source/Builders/OpenXRBuilderModule.cpp delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h rename Gems/OpenXRVk/Code/Source/{OpenXRActionsBindingAsset.cpp => OpenXRActionSetDescriptor.cpp} (54%) rename Gems/OpenXRVk/Code/Source/{OpenXRActionsBindingAsset.h => OpenXRActionSetDescriptor.h} (60%) create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.h create mode 100644 Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake create mode 100644 Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake diff --git a/Gems/OpenXRVk/Code/CMakeLists.txt b/Gems/OpenXRVk/Code/CMakeLists.txt index b2b4ddbb3..9deaf23d6 100644 --- a/Gems/OpenXRVk/Code/CMakeLists.txt +++ b/Gems/OpenXRVk/Code/CMakeLists.txt @@ -6,6 +6,10 @@ # # +# Queries the "gem_name" "version" values from the gem.json file for this gem +# They are set in the ${gem_name} and ${gem_version} variables +o3de_gem_setup("${Name}") + o3de_pal_dir(pal_include_dir ${CMAKE_CURRENT_LIST_DIR}/Include/OpenXRVk/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}") o3de_pal_dir(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}") @@ -19,7 +23,7 @@ if(NOT PAL_TRAIT_OPENXRVK_SUPPORTED) # Create stub modules. Once we support gem loading configuration, we can remove this stubbed targets ly_add_target( - NAME OpenXRVk ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} NAMESPACE Gem FILES_CMAKE openxrvk_stub_module.cmake @@ -36,7 +40,7 @@ if(NOT PAL_TRAIT_OPENXRVK_SUPPORTED) endif() ly_add_target( - NAME OpenXRVk.Static STATIC + NAME ${gem_name}.Static STATIC NAMESPACE Gem FILES_CMAKE openxrvk_private_common_files.cmake @@ -63,7 +67,7 @@ ly_add_target( ) ly_add_target( - NAME OpenXRVk ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} NAMESPACE Gem FILES_CMAKE openxrvk_private_common_shared_files.cmake @@ -76,7 +80,7 @@ ly_add_target( ${pal_include_dir} BUILD_DEPENDENCIES PRIVATE - Gem::OpenXRVk.Static + Gem::${gem_name}.Static ) # use the OpenXRVk module in all aliases: @@ -84,8 +88,55 @@ ly_create_alias(NAME OpenXRVk.Clients NAMESPACE Gem TARGETS Gem::OpenXRVk) ly_create_alias(NAME OpenXRVk.Unified NAMESPACE Gem TARGETS Gem::OpenXRVk) if(PAL_TRAIT_BUILD_HOST_TOOLS) - ly_create_alias(NAME OpenXRVk.Tools NAMESPACE Gem TARGETS Gem::OpenXRVk) - ly_create_alias(NAME OpenXRVk.Builders NAMESPACE Gem TARGETS Gem::OpenXRVk) + + ly_add_target( + NAME ${gem_name}.Builders.Static STATIC + NAMESPACE Gem + FILES_CMAKE + openxrvk_private_builder_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework + AZ::AssetBuilderSDK + ) + + ly_add_target( + NAME ${gem_name}.Builders GEM_MODULE + NAMESPACE Gem + FILES_CMAKE + openxrvk_shared_builder_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Source + PUBLIC + Include + BUILD_DEPENDENCIES + PRIVATE + AZ::AzCore + AZ::AzFramework + AZ::AssetBuilderSDK + Gem::${gem_name}.Builders.Static + ) + + # Inject the gem name into the Module source file + ly_add_source_properties( + SOURCES + Source/Builders/OpenXRBuilderModule.cpp + PROPERTY COMPILE_DEFINITIONS + VALUES + O3DE_GEM_NAME=${gem_name} + O3DE_GEM_VERSION=${gem_version} + ) + + # create an alias for the tool version + ly_create_alias(NAME ${gem_name}.Tools NAMESPACE Gem TARGETS Gem::${gem_name}.Builders) + endif() ################################################################################ diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp new file mode 100644 index 000000000..b5861cf8c --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include "OpenXRActionSetsAssetBuilder.h" + +#pragma optimize( "", off ) // GALIB + +namespace OpenXRVkBuilders +{ + // [[maybe_unused]] const char* AnyAssetBuilderName = "AnyAssetBuilder"; + // const char* AnyAssetBuilderJobKey = "Any Asset Builder"; + // const char* AnyAssetBuilderDefaultExtension = "azasset"; + // const char* AnyAssetSourceExtensions[] = + // { + // "azasset", + // "attimage", + // "azbuffer", + // }; + // const uint32_t NumberOfSourceExtensions = AZ_ARRAY_SIZE(AnyAssetSourceExtensions); + + void OpenXRActionSetsAssetBuilder::CreateJobs([[maybe_unused]] const AssetBuilderSDK::CreateJobsRequest& request, [[maybe_unused]] AssetBuilderSDK::CreateJobsResponse& response) const + { + + } + + + void OpenXRActionSetsAssetBuilder::ProcessJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const + { + + } + +} // namespace OpenXRVkBuilders + +#pragma optimize( "", on ) // GALIB diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h new file mode 100644 index 000000000..e6b9b218d --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +namespace OpenXRVkBuilders +{ + class OpenXRActionSetsAssetBuilder final + : public AssetBuilderSDK::AssetBuilderCommandBus::Handler + { + public: + AZ_TYPE_INFO(OpenXRActionSetsAssetBuilder, "{1D053000-7799-459D-B99B-FF6AE6394BC1}"); + + static constexpr char InteractionProfilesSourceFileExtension[] = "xrprofiles"; + static constexpr char ActionSetsSourceFileExtension[] = "xractions"; + + OpenXRActionSetsAssetBuilder() = default; + ~OpenXRActionSetsAssetBuilder() = default; + + // Asset Builder Callback Functions + void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; + void ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const; + + // AssetBuilderSDK::AssetBuilderCommandBus interface + void ShutDown() override { }; + + private: + AZ_DISABLE_COPY_MOVE(OpenXRActionSetsAssetBuilder); + }; + +} // namespace OpenXRVkBuilders diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp new file mode 100644 index 000000000..2ba0968a3 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include "OpenXRAssetBuildersSystemComponent.h" + +namespace OpenXRVkBuilders +{ + void OpenXRAssetBuildersSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(1) + ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })) + ; + } + } + + void OpenXRAssetBuildersSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("OpenXRAssetsBuilderService")); + } + + void OpenXRAssetBuildersSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("OpenXRAssetsBuilderService")); + } + + void OpenXRAssetBuildersSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + (void)required; + } + + void OpenXRAssetBuildersSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC("AssetCatalogService", 0xc68ffc57)); + } + + void OpenXRAssetBuildersSystemComponent::Init() + { + } + + void OpenXRAssetBuildersSystemComponent::Activate() + { + // Register Shader Asset Builder + AssetBuilderSDK::AssetBuilderDesc assetBuilderDescriptor; + assetBuilderDescriptor.m_name = "OpenXR ActionSets Builder"; + assetBuilderDescriptor.m_version = 1; // First versuib + assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRActionSetsAssetBuilder::InteractionProfilesSourceFileExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); + //assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRActionSetsAssetBuilder::ActionSetsSourceFileExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); + assetBuilderDescriptor.m_busId = azrtti_typeid(); + assetBuilderDescriptor.m_createJobFunction = AZStd::bind(&OpenXRActionSetsAssetBuilder::CreateJobs, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + assetBuilderDescriptor.m_processJobFunction = AZStd::bind(&OpenXRActionSetsAssetBuilder::ProcessJob, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + + m_actionSetsAssetBuilder.BusConnect(assetBuilderDescriptor.m_busId); + AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, assetBuilderDescriptor); + } + + void OpenXRAssetBuildersSystemComponent::Deactivate() + { + m_actionSetsAssetBuilder.BusDisconnect(); + } + +} // namespace AZ diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.h new file mode 100644 index 000000000..28bc3086c --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include "OpenXRActionSetsAssetBuilder.h" + +namespace OpenXRVkBuilders +{ + class OpenXRAssetBuildersSystemComponent + : public AZ::Component + { + public: + AZ_COMPONENT(OpenXRAssetBuildersSystemComponent, "{B046B553-CAB4-4AE5-9192-5E002771979B}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + protected: + //////////////////////////////////////////////////////////////////////// + // AzslShaderBuilderRequestBus interface implementation + + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + void Deactivate() override; + //////////////////////////////////////////////////////////////////////// + + + private: + OpenXRActionSetsAssetBuilder m_actionSetsAssetBuilder; + + }; + +} // namespace OpenXRVkBuilders diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRBuilderModule.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRBuilderModule.cpp new file mode 100644 index 000000000..43af7e544 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRBuilderModule.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include + +#include "OpenXRAssetBuildersSystemComponent.h" + +namespace OpenXRVkBuilders +{ + class OpenXRBuilderModule final + : public AZ::Module + { + public: + AZ_RTTI(OpenXRBuilderModule, "{43370465-DBF1-44BB-968D-97C0B42F5EA0}", AZ::Module); + AZ_CLASS_ALLOCATOR(OpenXRBuilderModule, AZ::SystemAllocator); + + OpenXRBuilderModule() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + m_descriptors.insert(m_descriptors.end(), { + OpenXRAssetBuildersSystemComponent::CreateDescriptor(), + }); + } + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } + }; +} // namespace OpenXRVkBuilders + +#if defined(O3DE_GEM_NAME) +AZ_DECLARE_MODULE_CLASS(AZ_JOIN(Gem_, O3DE_GEM_NAME, _Builders), OpenXRVkBuilders::OpenXRBuilderModule) +#else +AZ_DECLARE_MODULE_CLASS(Gem_OpenXRVk_Builders, OpenXRVkBuilders::OpenXRBuilderModule) +#endif diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp deleted file mode 100644 index 956d9fc29..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#include - -#include "KHRSimpleProfileSystemComponent.h" - -namespace OpenXRVk -{ - void KHRSimpleProfileSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("InteractionProfileProviderService")); - } - - void KHRSimpleProfileSystemComponent::Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1); - } - } - - void KHRSimpleProfileSystemComponent::Activate() - { - // m_name = { - // "Khronos Simple", // Khronos Simple Interaction Profile - // "/interaction_profiles/khr/simple_controller" - // }; - // - // m_userPaths = { - // {LeftHand, "/user/hand/left"}, - // {RightHand, "/user/hand/right"} - // }; - // - // const AZStd::vector commonPaths = { - // {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - // {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - // {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - // {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - // {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, - // }; - - m_name = { - "Oculus Touch", - "/interaction_profiles/oculus/touch_controller" - }; - - m_userPaths = { - {LeftHand, "/user/hand/left"}, - {RightHand, "/user/hand/right"} - }; - - const AZStd::vector commonPaths = { - {{"X Click", "/input/x/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - {{"X Touch", "/input/x/touch"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, // Just Testing. NOT supported in oculus - {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, - }; - m_componentPaths[LeftHand] = commonPaths; - m_componentPaths[RightHand] = commonPaths; - - OpenXRInteractionProfileBus::Handler::BusConnect(m_name.m_displayName); - } - - void KHRSimpleProfileSystemComponent::Deactivate() - { - OpenXRInteractionProfileBus::Handler::BusDisconnect(); - } - - /////////////////////////////////////////////////////////////////// - // OpenXRInteractionProfileBus::Handler overrides - //! Create OpenXRVk::Instance object - AZStd::string KHRSimpleProfileSystemComponent::GetName() const - { - return m_name.m_displayName; - } - - AZStd::vector KHRSimpleProfileSystemComponent::GetUserPaths() const - { - AZStd::vector retList; - retList.reserve(m_userPaths.size()); - for (const auto& pathTuple : m_userPaths) - { - retList.push_back(pathTuple.m_displayName); - } - return retList; - } - - AZStd::string KHRSimpleProfileSystemComponent::GetUserTopPath(const AZStd::string& userPathName) const - { - for (const auto& openxrPath : m_userPaths) - { - if (openxrPath.m_displayName == userPathName) - { - return openxrPath.m_xrRelativePath; - } - } - return AZStd::string(""); - } - - AZStd::vector KHRSimpleProfileSystemComponent::GetComponentPaths(const AZStd::string& userPath) const - { - if (!m_componentPaths.contains(userPath)) - { - AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); - return {}; - } - - const auto& paths = m_componentPaths.at(userPath); - AZStd::vector retList; - retList.reserve(paths.size()); - for (const auto& pathTuple : paths) - { - retList.push_back(pathTuple.m_displayName); - } - return retList; - } - - OpenXRInteractionProfile::ActionPathInfo KHRSimpleProfileSystemComponent::GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const - { - OpenXRInteractionProfile::ActionPathInfo retPathInfo; - - const auto* openxrUserPath = AZStd::find_if(m_userPaths.begin(), m_userPaths.end(), - [userPath](const OpenXRPath& entry) { - return entry.m_displayName == userPath; - }); - - if (openxrUserPath == m_userPaths.end()) - { - AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); - return retPathInfo; - } - - const auto& componentPaths = m_componentPaths.at(userPath); - const auto* openxrComponentPath = AZStd::find_if(componentPaths.begin(), componentPaths.end(), - [componentPath](const OpenXRComponentPath& entry) { - return entry.m_displayName == componentPath; - }); - - if (openxrComponentPath == componentPaths.end()) - { - AZ_Error(LogName, false, "Invalid component path [%s] for user path [%s].\n", componentPath.c_str(), userPath.c_str()); - return retPathInfo; - } - - retPathInfo.m_actionType = openxrComponentPath->m_actionType; - retPathInfo.m_absolutePath = openxrUserPath->m_xrRelativePath + openxrComponentPath->m_xrRelativePath; - - return retPathInfo; - } - - AZStd::string KHRSimpleProfileSystemComponent::GetInteractionProviderPath() const - { - return m_name.m_xrRelativePath; - } - /////////////////////////////////////////////////////////////////// -} \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h deleted file mode 100644 index 7bd7be9ff..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -#include - - -namespace OpenXRVk -{ - struct OpenXRPath - { - AZStd::string m_displayName; - //! Although this path is relative, - //! it should start with "/". - //! Examples: - //! 1- For a User Path this string would look like this: - //! "/user/hand/left" - //! 2- For a Component Path this string would look like this: - //! "/input/select/click" - AZStd::string m_xrRelativePath; - }; - - struct OpenXRComponentPath : public OpenXRPath - { - XrActionType m_actionType; - }; - - //! This system component provides data that can be used to pick xrActions - //! that will used at runtime for any given application. - class KHRSimpleProfileSystemComponent final - : public AZ::Component - , public OpenXRInteractionProfileBus::Handler - { - public: - AZ_COMPONENT(KHRSimpleProfileSystemComponent, "{123EDAF5-416B-4AEF-BEEC-03A8A8C71643}"); - - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void Reflect(AZ::ReflectContext* context); - - KHRSimpleProfileSystemComponent() = default; - ~KHRSimpleProfileSystemComponent() = default; - - ////////////////////////////////////////////////////////////////////////// - // Component - void Activate() override; - void Deactivate() override; - ////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////// - // OpenXRInteractionProfileBus::Handler overrides - //! Create OpenXRVk::Instance object - AZStd::string GetName() const override; - AZStd::vector GetUserPaths() const override; - AZStd::string GetUserTopPath(const AZStd::string& userPathName) const override; - AZStd::vector GetComponentPaths(const AZStd::string& userPath) const override; - OpenXRInteractionProfile::ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const override; - AZStd::string GetInteractionProviderPath() const override; - /////////////////////////////////////////////////////////////////// - - private: - static constexpr char LogName[] = "KHRSimpleProfileSystemComponent"; - - static constexpr AZStd::string_view LeftHand = "(L)"; - static constexpr AZStd::string_view RightHand = "(R)"; - OpenXRPath m_name; - AZStd::vector m_userPaths; - //! The key is a user path and the value is a list of component paths that exist - //! for said user path. - AZStd::unordered_map> m_componentPaths; - }; -}//namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp similarity index 54% rename from Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp index ff7c6b90f..efca6ff54 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp @@ -7,56 +7,56 @@ */ #include "InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h" -#include "OpenXRActionsBindingAsset.h" +#include "OpenXRActionSetDescriptor.h" namespace OpenXRVk { /////////////////////////////////////////////////////////// - /// OpenXRActionPath - void OpenXRActionPath::Reflect(AZ::ReflectContext* context) + /// OpenXRActionPathDescriptor + void OpenXRActionPathDescriptor::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { - serialize->Class() + serialize->Class() ->Version(1) - ->Field("Profile", &OpenXRActionPath::m_interactionProfile) - ->Field("UserPath", &OpenXRActionPath::m_userPath) - ->Field("ComponentPath", &OpenXRActionPath::m_componentPath) + ->Field("InteractionProfile", &OpenXRActionPathDescriptor::m_interactionProfileName) + ->Field("UserPath", &OpenXRActionPathDescriptor::m_userPathName) + ->Field("ComponentPath", &OpenXRActionPathDescriptor::m_componentPathName) ; AZ::EditContext* edit = serialize->GetEditContext(); if (edit) { - edit->Class("OpenXRActionPath", "A specific OpenXR I/O action path.") + edit->Class("OpenXRActionPathDescriptor", "A specific OpenXR I/O action path.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionPath::GetEditorText) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionPathDescriptor::GetEditorText) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_interactionProfile, "Interaction Profile", "The Interaction I/O profile") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnInteractionProfileSelected) - ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetInteractionProfiles) - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_userPath, "User Path", "Root path identifier") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnUserPathSelected) - ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetUserPaths) - ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPath::m_componentPath, "I/O Component Path", "I/O Component Path identifier") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPath::OnComponentPathSelected) - ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPath::GetComponentPaths) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_interactionProfileName, "Interaction Profile", "The name of the Interaction Profile.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnInteractionProfileSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetInteractionProfiles) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_userPathName, "User Path", "Name of the User Path.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnUserPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetUserPaths) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_componentPathName, "I/O Component Path", "The name of I/O Component Path.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnComponentPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetComponentPaths) ; } } } - AZStd::string OpenXRActionPath::GetEditorText() const + AZStd::string OpenXRActionPathDescriptor::GetEditorText() const { - return AZStd::string::format("%s %s %s", m_interactionProfile.c_str(), m_userPath.c_str(), m_componentPath.c_str()); + return AZStd::string::format("%s %s %s", m_interactionProfileName.c_str(), m_userPathName.c_str(), m_componentPathName.c_str()); } - AZ::Crc32 OpenXRActionPath::OnInteractionProfileSelected() + AZ::Crc32 OpenXRActionPathDescriptor::OnInteractionProfileSelected() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } - AZStd::vector OpenXRActionPath::GetInteractionProfiles() const + AZStd::vector OpenXRActionPathDescriptor::GetInteractionProfiles() const { auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); if (!interactionProviderIface) @@ -66,12 +66,12 @@ namespace OpenXRVk return interactionProviderIface->GetInteractionProfileNames(); } - AZ::Crc32 OpenXRActionPath::OnUserPathSelected() + AZ::Crc32 OpenXRActionPathDescriptor::OnUserPathSelected() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } - AZStd::vector OpenXRActionPath::GetUserPaths() const + AZStd::vector OpenXRActionPathDescriptor::GetUserPaths() const { AZStd::vector retList; auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); @@ -80,7 +80,7 @@ namespace OpenXRVk return retList; } - const auto * profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfile); + const auto * profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfileName); if (!profileDescriptor) { return retList; @@ -92,12 +92,12 @@ namespace OpenXRVk return retList; } - AZ::Crc32 OpenXRActionPath::OnComponentPathSelected() + AZ::Crc32 OpenXRActionPathDescriptor::OnComponentPathSelected() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } - AZStd::vector OpenXRActionPath::GetComponentPaths() const + AZStd::vector OpenXRActionPathDescriptor::GetComponentPaths() const { AZStd::vector retList; auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); @@ -106,12 +106,12 @@ namespace OpenXRVk return retList; } - const auto* profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfile); + const auto* profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfileName); if (!profileDescriptor) { return retList; } - const auto* userPathDescriptor = profileDescriptor->GetUserPathDescriptor(m_userPath); + const auto* userPathDescriptor = profileDescriptor->GetUserPathDescriptor(m_userPathName); if (!userPathDescriptor) { return retList; @@ -129,129 +129,98 @@ namespace OpenXRVk return retList; } - /// OpenXRActionPath + /// OpenXRActionPathDescriptor /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// - /// OpenXRAction - void OpenXRAction::Reflect(AZ::ReflectContext* context) + /// OpenXRActionDescriptor + void OpenXRActionDescriptor::Reflect(AZ::ReflectContext* context) { - OpenXRActionPath::Reflect(context); + OpenXRActionPathDescriptor::Reflect(context); AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { - serialize->Class() + serialize->Class() ->Version(1) - ->Field("Name", &OpenXRAction::m_name) - ->Field("LocalizedName", &OpenXRAction::m_localizedName) - ->Field("ActionPaths", &OpenXRAction::m_actionPaths) + ->Field("Name", &OpenXRActionDescriptor::m_name) + ->Field("LocalizedName", &OpenXRActionDescriptor::m_localizedName) + ->Field("ActionPathDescriptors", &OpenXRActionDescriptor::m_actionPathDescriptors) ; AZ::EditContext* edit = serialize->GetEditContext(); if (edit) { - edit->Class("OpenXRAction", "An action bound to one or more OpenXR I/O Paths.") + edit->Class("OpenXRActionDescriptor", "An action bound to one or more OpenXR Action Paths.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRAction::GetEditorText) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionDescriptor::GetEditorText) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_name, "Name", "Runtime identifier for this action.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_name, "Name", "Runtime identifier for this action.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_localizedName, "Localized Name", "User friendly display name.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_localizedName, "Localized Name", "User friendly display name.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRAction::m_actionPaths, "Action Paths", "List of action paths bound to this action") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_actionPathDescriptors, "Action Paths", "List of action paths bound to this action") ; } } } - AZStd::string OpenXRAction::GetEditorText() const + AZStd::string OpenXRActionDescriptor::GetEditorText() const { if (!m_localizedName.empty()) { return m_localizedName; } - return m_name.empty() ? "" : m_name; + return m_name.empty() ? "" : m_name; } - /// OpenXRAction + /// OpenXRActionDescriptor /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// - /// OpenXRActionSet - void OpenXRActionSet::Reflect(AZ::ReflectContext* context) + /// OpenXRActionSetDescriptor + void OpenXRActionSetDescriptor::Reflect(AZ::ReflectContext* context) { - OpenXRAction::Reflect(context); + OpenXRActionDescriptor::Reflect(context); AZ::SerializeContext* serialize = azrtti_cast(context); if (serialize) { - serialize->Class() + serialize->Class() ->Version(1) - ->Field("Name", &OpenXRActionSet::m_name) - ->Field("LocalizedName", &OpenXRActionSet::m_localizedName) - ->Field("Priority", &OpenXRActionSet::m_priority) - ->Field("Actions", &OpenXRActionSet::m_actions) + ->Field("Name", &OpenXRActionSetDescriptor::m_name) + ->Field("LocalizedName", &OpenXRActionSetDescriptor::m_localizedName) + ->Field("Priority", &OpenXRActionSetDescriptor::m_priority) + ->Field("ActionDescriptors", &OpenXRActionSetDescriptor::m_actionDescriptors) ; AZ::EditContext* edit = serialize->GetEditContext(); if (edit) { - edit->Class("OpenXRActionSet", "A group of OpenXR Actions that can be selectively activated/deactivated at runtime.") + edit->Class("OpenXRActionSetDescriptor", "A group of OpenXR Actions that can be selectively activated/deactivated at runtime.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionSet::GetEditorText) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionSetDescriptor::GetEditorText) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_name, "Name", "Runtime identifier for this action set.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_name, "Name", "Runtime identifier for this action set.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_localizedName, "Localized Name", "Action set display name.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_localizedName, "Localized Name", "Action set display name.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) - ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSet::m_priority, "Priority", "The higher this value the higher the priority.") - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSet::m_actions, "Actions", "List of actions for this action set.") + ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSetDescriptor::m_priority, "Priority", "The higher this value the higher the priority.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_actionDescriptors, "Actions", "List of actions for this action set.") ; } } } - AZStd::string OpenXRActionSet::GetEditorText() const + AZStd::string OpenXRActionSetDescriptor::GetEditorText() const { if (!m_localizedName.empty()) { return m_localizedName; } - return m_name.empty() ? "" : m_name; + return m_name.empty() ? "" : m_name; } - /// OpenXRActionSet - /////////////////////////////////////////////////////////// - - - /////////////////////////////////////////////////////////// - /// OpenXRActionBindingsAsset - void OpenXRActionBindingsAsset::Reflect(AZ::ReflectContext* context) - { - OpenXRActionSet::Reflect(context); - - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) - { - serialize->Class() - ->Version(1) - ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) - ->Field("ActionSets", &OpenXRActionBindingsAsset::m_actionSets) - ; - - AZ::EditContext* edit = serialize->GetEditContext(); - if (edit) - { - edit->Class( - s_assetTypeName, "Defines the OpenXR Actions an application cares about.") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionBindingsAsset::m_actionSets, "Action Sets", "List of action sets.") - ; - } - } - } - /// OpenXRActionBindingsAsset + /// OpenXRActionSetDescriptor /////////////////////////////////////////////////////////// } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.h b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.h similarity index 60% rename from Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.h rename to Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.h index a2b6ade17..3816fee6e 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsBindingAsset.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.h @@ -17,19 +17,19 @@ namespace OpenXRVk { - class OpenXRActionPath final + class OpenXRActionPathDescriptor final { public: - AZ_RTTI(OpenXRActionPath, "{F25D6382-C9E0-414B-A542-1758F5477D03}"); - virtual ~OpenXRActionPath() = default; + AZ_RTTI(OpenXRActionPathDescriptor, "{F25D6382-C9E0-414B-A542-1758F5477D03}"); + virtual ~OpenXRActionPathDescriptor() = default; static void Reflect(AZ::ReflectContext* reflection); AZStd::string GetEditorText() const; - AZStd::string m_interactionProfile; - AZStd::string m_userPath; - AZStd::string m_componentPath; + AZStd::string m_interactionProfileName; + AZStd::string m_userPathName; + AZStd::string m_componentPathName; private: AZ::Crc32 OnInteractionProfileSelected(); @@ -42,11 +42,11 @@ namespace OpenXRVk AZStd::vector GetComponentPaths() const; }; - class OpenXRAction final + class OpenXRActionDescriptor final { public: - AZ_RTTI(OpenXRAction, "{90BBF6F6-C7D6-4F64-B784-CE03F86DC36B}"); - virtual ~OpenXRAction() = default; + AZ_RTTI(OpenXRActionDescriptor, "{90BBF6F6-C7D6-4F64-B784-CE03F86DC36B}"); + virtual ~OpenXRActionDescriptor() = default; static void Reflect(AZ::ReflectContext* reflection); @@ -61,14 +61,14 @@ namespace OpenXRVk //! can only be added if they can map to a boolean. //! Another important case is if the this is a haptic feedback action (Output), then //! subsequent action paths can only be of type haptic feedback actions. - AZStd::vector m_actionPaths; + AZStd::vector m_actionPathDescriptors; }; - class OpenXRActionSet final + class OpenXRActionSetDescriptor final { public: - AZ_RTTI(OpenXRActionSet, "{3A08BC1F-656F-441F-89C3-829F95B9B329}"); - virtual ~OpenXRActionSet() = default; + AZ_RTTI(OpenXRActionSetDescriptor, "{3A08BC1F-656F-441F-89C3-829F95B9B329}"); + virtual ~OpenXRActionSetDescriptor() = default; static void Reflect(AZ::ReflectContext* reflection); @@ -77,22 +77,7 @@ namespace OpenXRVk AZStd::string m_name; // Regular char* AZStd::string m_localizedName; // UTF-8 string. uint32_t m_priority = 0; // Higher values mean higher priority. - AZStd::vector m_actions; + AZStd::vector m_actionDescriptors; }; - //! This asset defines a list of OpenXR Action Sets that an application supports - //! regarding inputs and haptics. - class OpenXRActionBindingsAsset final - : public AZ::Data::AssetData - { - public: - AZ_RTTI(OpenXRActionBindingsAsset, "{C2DEE370-6151-4701-AEA5-AEA3CA247CFF}", AZ::Data::AssetData); - AZ_CLASS_ALLOCATOR(OpenXRActionBindingsAsset, AZ::SystemAllocator); - static void Reflect(AZ::ReflectContext* context); - - static constexpr char s_assetTypeName[] = "OpenXR Actions Binding Asset"; - static constexpr char s_assetExtension[] = "xractions"; - - AZStd::vector m_actionSets; - }; }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp new file mode 100644 index 000000000..4f0ed3886 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include +#include +#include + +#include "OpenXRActionSetsAsset.h" + +namespace OpenXRVk +{ + /////////////////////////////////////////////////////////// + /// OpenXRActionBindingsAsset + void OpenXRActionSetsAsset::Reflect(AZ::ReflectContext* context) + { + OpenXRActionSetDescriptor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) + ->Field("ActionSetDescriptors", &OpenXRActionSetsAsset::m_actionSetDescriptors) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + s_assetTypeName, "Defines the OpenXR Actions an application cares about.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetsAsset::m_actionSetDescriptors, "Action Sets", "List of action sets.") + ; + } + } + } + /// OpenXRActionBindingsAsset + /////////////////////////////////////////////////////////// + +} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.h b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.h new file mode 100644 index 000000000..cc94bdd4d --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include +#include + +#include "OpenXRActionSetDescriptor.h" + +namespace OpenXRVk +{ + //! This asset defines a list of OpenXR Action Sets that an application supports + //! regarding inputs and haptics. + class OpenXRActionSetsAsset final + : public AZ::Data::AssetData + { + public: + AZ_RTTI(OpenXRActionSetsAsset, "{C2DEE370-6151-4701-AEA5-AEA3CA247CFF}", AZ::Data::AssetData); + AZ_CLASS_ALLOCATOR(OpenXRActionSetsAsset, AZ::SystemAllocator); + static void Reflect(AZ::ReflectContext* context); + + static constexpr char s_assetTypeName[] = "OpenXR Action Sets Asset"; + static constexpr char s_assetExtension[] = "xractions"; + + AZStd::vector m_actionSetDescriptors; + }; +}// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index e57bdb4db..8ef44cb3c 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -10,7 +10,7 @@ #include -#include "OpenXRActionsBindingAsset.h" +#include "OpenXRActionSetsAsset.h" #include #include #include diff --git a/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake new file mode 100644 index 000000000..47ef6efed --- /dev/null +++ b/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake @@ -0,0 +1,14 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Source/Builders/OpenXRActionSetsAssetBuilder.cpp + Source/Builders/OpenXRActionSetsAssetBuilder.h + Source/Builders/OpenXRAssetBuildersSystemComponent.cpp + Source/Builders/OpenXRAssetBuildersSystemComponent.h +) diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index 2f491f28d..8a54ab605 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -30,15 +30,17 @@ set(FILES Source/OpenXRVkSwapChain.cpp Source/OpenXRVkSystemComponent.cpp Source/OpenXRVkUtils.cpp - Source/InteractionProfiles/KHRSimpleProfileSystemComponent.cpp - Source/InteractionProfiles/KHRSimpleProfileSystemComponent.h + Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp + Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h - Source/OpenXRActionsBindingAsset.cpp - Source/OpenXRActionsBindingAsset.h + Source/OpenXRActionSetsAsset.cpp + Source/OpenXRActionSetsAsset.h + Source/OpenXRActionSetDescriptor.cpp + Source/OpenXRActionSetDescriptor.h Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h Source/OpenXRActionsManager.cpp diff --git a/Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake b/Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake new file mode 100644 index 000000000..02da43db7 --- /dev/null +++ b/Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake @@ -0,0 +1,11 @@ +# +# Copyright (c) Contributors to the Open 3D Engine Project. +# For complete copyright and license terms please see the LICENSE at the root of this distribution. +# +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# + +set(FILES + Source/Builders/OpenXRBuilderModule.cpp +) From 54a843017e5a9032c64a3e8ccd5a88da28fe98e3 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 23 Jan 2024 15:40:27 -0600 Subject: [PATCH 15/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- Gems/OpenXRVk/Code/CMakeLists.txt | 13 +- .../OpenXRVk}/OpenXRActionSetDescriptor.h | 0 .../OpenXRVk}/OpenXRActionSetsAsset.h | 0 .../OpenXRInteractionProfileDescriptor.h | 10 +- .../OpenXRInteractionProfilesAsset.h | 2 +- .../Code/Include/OpenXRVk/OpenXRVkSession.h | 4 +- .../OpenXRVk/OpenXRVkSystemComponent.h | 8 +- .../Builders/OpenXRActionSetsAssetBuilder.cpp | 83 ++++- .../Builders/OpenXRActionSetsAssetBuilder.h | 8 +- .../OpenXRAssetBuildersSystemComponent.cpp | 6 +- .../OpenXRInteractionProfileDescriptor.cpp | 2 +- .../OpenXRInteractionProfilesAsset.cpp | 2 +- ...enXRInteractionProfilesProviderInterface.h | 2 +- ...ractionProfilesProviderSystemComponent.cpp | 284 ++++++++++++++++++ ...teractionProfilesProviderSystemComponent.h | 101 +++++++ .../Code/Source/OpenXRActionSetDescriptor.cpp | 2 +- .../Code/Source/OpenXRActionSetsAsset.cpp | 2 +- .../Code/Source/OpenXRActionsManager.cpp | 2 +- .../Code/Source/OpenXRActionsManager.h | 9 +- Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp | 7 +- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 59 ++-- .../Code/Source/OpenXRVkSystemComponent.cpp | 8 +- .../Code/openxrvk_private_common_files.cmake | 16 +- 23 files changed, 558 insertions(+), 72 deletions(-) rename Gems/OpenXRVk/Code/{Source => Include/OpenXRVk}/OpenXRActionSetDescriptor.h (100%) rename Gems/OpenXRVk/Code/{Source => Include/OpenXRVk}/OpenXRActionSetsAsset.h (100%) rename Gems/OpenXRVk/Code/{Source/InteractionProfiles => Include/OpenXRVk}/OpenXRInteractionProfileDescriptor.h (93%) rename Gems/OpenXRVk/Code/{Source/InteractionProfiles => Include/OpenXRVk}/OpenXRInteractionProfilesAsset.h (97%) create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp create mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h diff --git a/Gems/OpenXRVk/Code/CMakeLists.txt b/Gems/OpenXRVk/Code/CMakeLists.txt index 9deaf23d6..c6a72fe26 100644 --- a/Gems/OpenXRVk/Code/CMakeLists.txt +++ b/Gems/OpenXRVk/Code/CMakeLists.txt @@ -88,7 +88,8 @@ ly_create_alias(NAME OpenXRVk.Clients NAMESPACE Gem TARGETS Gem::OpenXRVk) ly_create_alias(NAME OpenXRVk.Unified NAMESPACE Gem TARGETS Gem::OpenXRVk) if(PAL_TRAIT_BUILD_HOST_TOOLS) - + + # ly_add_target( NAME ${gem_name}.Builders.Static STATIC NAMESPACE Gem @@ -104,8 +105,12 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) AZ::AzCore AZ::AzFramework AZ::AssetBuilderSDK + ${openxr_dependency} + Gem::${gem_name}.Static ) + # It is very important that the target name ends with "*.Buiders" + # because it maakes the module loadable by AssetBuilder.exe. ly_add_target( NAME ${gem_name}.Builders GEM_MODULE NAMESPACE Gem @@ -134,8 +139,10 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) O3DE_GEM_VERSION=${gem_version} ) - # create an alias for the tool version - ly_create_alias(NAME ${gem_name}.Tools NAMESPACE Gem TARGETS Gem::${gem_name}.Builders) + # Create an alias for the tool version. + # This is necessary so the Editor loads the Module. And the trick + # is to make sure the alias name ends in ".Tools" + ly_create_alias(NAME ${gem_name}.Tools NAMESPACE Gem TARGETS Gem::${gem_name}) endif() diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetDescriptor.h similarity index 100% rename from Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetDescriptor.h diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h similarity index 100% rename from Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h similarity index 93% rename from Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h index 2360a7112..ee04c83f7 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h @@ -27,11 +27,11 @@ namespace OpenXRVk static void Reflect(AZ::ReflectContext* reflection); - static constexpr AZStd::string_view s_TypeBoolStr{ "XrBool" }; - static constexpr AZStd::string_view s_TypeFloatStr = "XrFloat"; - static constexpr AZStd::string_view s_TypeVector2Str = "XrVector2"; - static constexpr AZStd::string_view s_TypePoseStr = "XrPose"; - static constexpr AZStd::string_view s_TypeVibrationStr = "XrVibration"; + static constexpr AZStd::string_view s_TypeBoolStr = "Boolean"; + static constexpr AZStd::string_view s_TypeFloatStr = "Float"; + static constexpr AZStd::string_view s_TypeVector2Str = "Vector2"; + static constexpr AZStd::string_view s_TypePoseStr = "Pose"; + static constexpr AZStd::string_view s_TypeVibrationStr = "Vibration"; //! Helper method static XrActionType GetXrActionType(AZStd::string_view actionTypeStr); diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h similarity index 97% rename from Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h index 3d548e9b2..0a0445732 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h @@ -17,7 +17,7 @@ #include -#include "InteractionProfiles/OpenXRInteractionProfileDescriptor.h" +#include namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h index e97929cd8..7c64f4f88 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h @@ -98,8 +98,8 @@ namespace OpenXRVk XrInstance m_xrInstance = XR_NULL_HANDLE; XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR }; - AZStd::unique_ptr m_actionsMgr; - AZStd::unique_ptr m_visualizedSpacesMgr; + //AZStd::unique_ptr m_actionsMgr; + //AZStd::unique_ptr m_visualizedSpacesMgr; // Application defined base space that will used to calculate // the relative pose of all other spaces. diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h index 1b0764f79..5e1f0464a 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h @@ -12,8 +12,9 @@ #include #include #include -#include "OpenXRActionsBindingAsset.h" -#include "InteractionProfiles/OpenXRInteractionProfilesAsset.h" + +#include +#include namespace OpenXRVk { @@ -66,7 +67,8 @@ namespace OpenXRVk private: XR::Ptr m_instance; - AZStd::unique_ptr> m_actionsBindingAssetHandler; + AZStd::unique_ptr m_interactionProfilesAssetHandler; + AZStd::unique_ptr> m_actionSetsAssetHandler; }; } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp index b5861cf8c..3ca1d02cf 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp @@ -6,8 +6,11 @@ * */ +#include #include +#include + #include "OpenXRActionSetsAssetBuilder.h" #pragma optimize( "", off ) // GALIB @@ -25,15 +28,91 @@ namespace OpenXRVkBuilders // }; // const uint32_t NumberOfSourceExtensions = AZ_ARRAY_SIZE(AnyAssetSourceExtensions); - void OpenXRActionSetsAssetBuilder::CreateJobs([[maybe_unused]] const AssetBuilderSDK::CreateJobsRequest& request, [[maybe_unused]] AssetBuilderSDK::CreateJobsResponse& response) const + void OpenXRActionSetsAssetBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { + //! First get the extension + constexpr bool includeDot = false; + AZStd::string fileExtension; + bool result = AZ::StringFunc::Path::GetExtension(request.m_sourceFile.c_str(), fileExtension, includeDot); + if (result && (fileExtension == OpenXRVk::OpenXRInteractionProfilesAsset::s_assetExtension)) + { + CreateInteractionProfilesJobs(request, response); + return; + } + //! Unknown extension. + AZ_Error(LogName, false, "Unknown file extension [%s] for this builder. Source file [%s]", fileExtension.c_str(), request.m_sourceFile.c_str()); + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed; } - void OpenXRActionSetsAssetBuilder::ProcessJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const + void OpenXRActionSetsAssetBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const + { + //! First get the extension + constexpr bool includeDot = false; + AZStd::string fileExtension; + bool result = AZ::StringFunc::Path::GetExtension(request.m_sourceFile.c_str(), fileExtension, includeDot); + if (result && (fileExtension == OpenXRVk::OpenXRInteractionProfilesAsset::s_assetExtension)) + { + ProcessInteractionProfilesJob(request, response); + } + } + + void OpenXRActionSetsAssetBuilder::CreateInteractionProfilesJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const + { + for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms) + { + AssetBuilderSDK::JobDescriptor jobDescriptor; + // Very high priority because this asset is required to initialize the OpenXR runtime + // and initialize the I/O actions system. + jobDescriptor.m_priority = 1000; + jobDescriptor.m_critical = true; + jobDescriptor.m_jobKey = JobKey; + jobDescriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str()); + response.m_createJobOutputs.emplace_back(AZStd::move(jobDescriptor)); + } // for all request.m_enabledPlatforms + + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; + } + + void OpenXRActionSetsAssetBuilder::ProcessInteractionProfilesJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const { + // Open the file, and make sure there's no redundant data, the OpenXR Paths are well formatted, etc. + auto interactionProfilesAssetPtr = AZ::Utils::LoadObjectFromFile(request.m_fullPath); + //AZ_Error(LogName, false, "The interaction profiles contain %zu profiles", interactionProfilesAssetPtr->m_interactionProfileDescriptors.size()); + + // FIXME: TODO: All this builder is supposed to do is validate the data. + // If the validation passes, then we simply generate a product which is just a copy of the original. + + // We keep exact same asset name and extension. + AZStd::string assetFileName; + AZ::StringFunc::Path::GetFullFileName(request.m_fullPath.c_str(), assetFileName); + + // Construct product full path + AZStd::string assetOutputPath; + AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), assetFileName.c_str(), assetOutputPath, true); + + bool result = AZ::Utils::SaveObjectToFile(assetOutputPath, AZ::DataStream::ST_XML, interactionProfilesAssetPtr); + if (result == false) + { + AZ_Error(LogName, false, "Failed to save asset to %s", assetOutputPath.c_str()); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } + // This step is very important, because it declares product dependency between ShaderAsset and the root ShaderVariantAssets (one for each supervariant). + // This will guarantee that when the ShaderAsset is loaded at runtime, the ShaderAsset will report OnAssetReady only after the root ShaderVariantAssets + // are already fully loaded and ready. + AssetBuilderSDK::JobProduct jobProduct; + if (!AssetBuilderSDK::OutputObject(interactionProfilesAssetPtr, assetOutputPath, azrtti_typeid(), + aznumeric_cast(0), jobProduct)) + { + AZ_Error(LogName, false, "FIXME this message."); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } + response.m_outputProducts.emplace_back(AZStd::move(jobProduct)); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; } } // namespace OpenXRVkBuilders diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h index e6b9b218d..2eff33cf4 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h @@ -19,8 +19,7 @@ namespace OpenXRVkBuilders public: AZ_TYPE_INFO(OpenXRActionSetsAssetBuilder, "{1D053000-7799-459D-B99B-FF6AE6394BC1}"); - static constexpr char InteractionProfilesSourceFileExtension[] = "xrprofiles"; - static constexpr char ActionSetsSourceFileExtension[] = "xractions"; + static constexpr const char* JobKey = "XR Action Sets Asset"; OpenXRActionSetsAssetBuilder() = default; ~OpenXRActionSetsAssetBuilder() = default; @@ -34,6 +33,11 @@ namespace OpenXRVkBuilders private: AZ_DISABLE_COPY_MOVE(OpenXRActionSetsAssetBuilder); + + static constexpr char LogName[] = "OpenXRActionSetsAssetBuilder"; + + void CreateInteractionProfilesJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; + void ProcessInteractionProfilesJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const; }; } // namespace OpenXRVkBuilders diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp index 2ba0968a3..1c2d768e7 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp @@ -9,12 +9,16 @@ #include #include +#include + #include "OpenXRAssetBuildersSystemComponent.h" namespace OpenXRVkBuilders { void OpenXRAssetBuildersSystemComponent::Reflect(AZ::ReflectContext* context) { + OpenXRVk::OpenXRInteractionProfilesAsset::Reflect(context); + if (AZ::SerializeContext* serialize = azrtti_cast(context)) { serialize->Class() @@ -41,7 +45,7 @@ namespace OpenXRVkBuilders void OpenXRAssetBuildersSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { - dependent.push_back(AZ_CRC("AssetCatalogService", 0xc68ffc57)); + dependent.push_back(AZ_CRC("AssetCatalogService")); } void OpenXRAssetBuildersSystemComponent::Init() diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp index c9587784f..5859571b6 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp @@ -8,7 +8,7 @@ #include -#include "OpenXRInteractionProfileDescriptor.h" +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp index 35a717ee2..8f98060c2 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp @@ -6,7 +6,7 @@ * */ -#include "OpenXRInteractionProfilesAsset.h" +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h index 060b342fa..95e4b97ca 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h @@ -14,7 +14,7 @@ #include #include -#include "OpenXRInteractionProfileDescriptor.h" +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp new file mode 100644 index 000000000..0ae07c23d --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + + +#include +#include + +#include "OpenXRInteractionProfilesProviderSystemComponent.h" + +namespace OpenXRVk +{ + void OpenXRInteractionProfilesProviderSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1); + } + } + + void OpenXRInteractionProfilesProviderSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC_CE("InteractionProfileProviderService")); + } + + void OpenXRInteractionProfilesProviderSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("InteractionProfileProviderService")); + } + + void OpenXRInteractionProfilesProviderSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + (void)required; + } + + void OpenXRInteractionProfilesProviderSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + dependent.push_back(AZ_CRC("AssetCatalogService")); + } + + void OpenXRInteractionProfilesProviderSystemComponent::Activate() + { + // m_name = { + // "Khronos Simple", // Khronos Simple Interaction Profile + // "/interaction_profiles/khr/simple_controller" + // }; + // + // m_userPaths = { + // {LeftHand, "/user/hand/left"}, + // {RightHand, "/user/hand/right"} + // }; + // + // const AZStd::vector commonPaths = { + // {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + // {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + // {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + // {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + // {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, + // }; + + // m_name = { + // "Oculus Touch", + // "/interaction_profiles/oculus/touch_controller" + // }; + // + // m_userPaths = { + // {LeftHand, "/user/hand/left"}, + // {RightHand, "/user/hand/right"} + // }; + // + // const AZStd::vector commonPaths = { + // {{"X Click", "/input/x/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + // {{"X Touch", "/input/x/touch"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + // {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, // Just Testing. NOT supported in oculus + // {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, + // {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + // {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, + // {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, + // }; + // m_componentPaths[LeftHand] = commonPaths; + // m_componentPaths[RightHand] = commonPaths; + + AzFramework::AssetCatalogEventBus::Handler::BusConnect(); + } + + void OpenXRInteractionProfilesProviderSystemComponent::Deactivate() + { + if (AzFramework::AssetCatalogEventBus::Handler::BusIsConnected()) + { + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + } + + if (AZ::Data::AssetBus::Handler::BusIsConnected()) + { + AZ::Data::AssetBus::Handler::BusDisconnect(); + } + } + + AZStd::string OpenXRInteractionProfilesProviderSystemComponent::GetDefaultInteractionProfilesAssetPath() + { + // TODO: Make this user configurable with registry property. + return "system.xrprofiles"; + } + + ////////////////////////////////////////////////////////////////////////// + // AssetCatalogEventBus + void OpenXRInteractionProfilesProviderSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/) + { + // No need to staying connected anymore. + AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); + + // Load, asychronously, the asset that contains the list of supported + // OpenXR Interaction Profiles. + constexpr bool AutoGenerateId = false; + AZ::Data::AssetId assetId; + const auto assetPath = GetDefaultInteractionProfilesAssetPath(); + AZ::Data::AssetCatalogRequestBus::BroadcastResult( + assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, + assetPath.c_str(), azrtti_typeid(), AutoGenerateId); + if (!assetId.IsValid()) + { + AZ_Assert(false, "Failed to find the interaction profiles asset with path [%s]", assetPath.c_str()); + AZ_Error(LogName, false, "Failed to find the interaction profiles asset with path [%s]", assetPath.c_str()); + return; + } + + AZ::Data::AssetBus::Handler::BusConnect(assetId); + + m_interactionProfilesAsset = AZ::Data::AssetManager::Instance().GetAsset(assetId, AZ::Data::AssetLoadBehavior::QueueLoad); + + } + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + // AZ::Data::AssetBus::Handler overrides + void OpenXRInteractionProfilesProviderSystemComponent::OnAssetReady(AZ::Data::Asset asset) + { + AZ::Data::AssetBus::Handler::BusDisconnect(); + m_interactionProfilesAsset = asset; + } + + void OpenXRInteractionProfilesProviderSystemComponent::OnAssetError(AZ::Data::Asset asset) + { + AZ::Data::AssetBus::Handler::BusDisconnect(); + m_interactionProfilesAsset = {}; + AZ_Assert(false, "Failed to load interaction profiles asset"); + AZ_Error(LogName, false, "Failed to load interaction profiles asset"); + + } + ////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + // OpenXRInteractionProfileBus::Handler overrides + const AZStd::vector& OpenXRInteractionProfilesProviderSystemComponent::GetInteractionProfileNames() const + { + static AZStd::vector profileNames; + + if (!m_interactionProfilesAsset.IsReady()) + { + AZ_Error(LogName, false, "Interaction Profiles Asset Doesn't exist or it is not ready yet."); + return profileNames; + } + + if (profileNames.empty()) + { + for (const auto& profileDescriptor : m_interactionProfilesAsset->m_interactionProfileDescriptors) + { + profileNames.push_back(profileDescriptor.m_uniqueName); + } + } + + return profileNames; + } + + const OpenXRInteractionProfileDescriptor* OpenXRInteractionProfilesProviderSystemComponent::GetInteractionProfileDescriptor(const AZStd::string profileName) const + { + if (!m_interactionProfilesAsset.IsReady()) + { + AZ_Error(LogName, false, "Interaction Profiles Asset Doesn't exist or it is not ready yet."); + return nullptr; + } + + for (const auto& profileDescriptor : m_interactionProfilesAsset->m_interactionProfileDescriptors) + { + if (profileName == profileDescriptor.m_uniqueName) + { + return &profileDescriptor; + } + } + + AZ_Error(LogName, false, "Interaction Profile with name [%s] doesn't exist.", profileName.c_str()); + return nullptr; + } + + // AZStd::string KHRSimpleProfileSystemComponent::GetName() const + // { + // return m_name.m_displayName; + // } + // + // AZStd::vector KHRSimpleProfileSystemComponent::GetUserPaths() const + // { + // AZStd::vector retList; + // retList.reserve(m_userPaths.size()); + // for (const auto& pathTuple : m_userPaths) + // { + // retList.push_back(pathTuple.m_displayName); + // } + // return retList; + // } + // + // AZStd::string KHRSimpleProfileSystemComponent::GetUserTopPath(const AZStd::string& userPathName) const + // { + // for (const auto& openxrPath : m_userPaths) + // { + // if (openxrPath.m_displayName == userPathName) + // { + // return openxrPath.m_xrRelativePath; + // } + // } + // return AZStd::string(""); + // } + // + // AZStd::vector KHRSimpleProfileSystemComponent::GetComponentPaths(const AZStd::string& userPath) const + // { + // if (!m_componentPaths.contains(userPath)) + // { + // AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); + // return {}; + // } + // + // const auto& paths = m_componentPaths.at(userPath); + // AZStd::vector retList; + // retList.reserve(paths.size()); + // for (const auto& pathTuple : paths) + // { + // retList.push_back(pathTuple.m_displayName); + // } + // return retList; + // } + // + // OpenXRInteractionProfile::ActionPathInfo KHRSimpleProfileSystemComponent::GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const + // { + // OpenXRInteractionProfile::ActionPathInfo retPathInfo; + // + // const auto* openxrUserPath = AZStd::find_if(m_userPaths.begin(), m_userPaths.end(), + // [userPath](const OpenXRPath& entry) { + // return entry.m_displayName == userPath; + // }); + // + // if (openxrUserPath == m_userPaths.end()) + // { + // AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); + // return retPathInfo; + // } + // + // const auto& componentPaths = m_componentPaths.at(userPath); + // const auto* openxrComponentPath = AZStd::find_if(componentPaths.begin(), componentPaths.end(), + // [componentPath](const OpenXRComponentPath& entry) { + // return entry.m_displayName == componentPath; + // }); + // + // if (openxrComponentPath == componentPaths.end()) + // { + // AZ_Error(LogName, false, "Invalid component path [%s] for user path [%s].\n", componentPath.c_str(), userPath.c_str()); + // return retPathInfo; + // } + // + // retPathInfo.m_actionType = openxrComponentPath->m_actionType; + // retPathInfo.m_absolutePath = openxrUserPath->m_xrRelativePath + openxrComponentPath->m_xrRelativePath; + // + // return retPathInfo; + // } + // + // AZStd::string KHRSimpleProfileSystemComponent::GetInteractionProviderPath() const + // { + // return m_name.m_xrRelativePath; + // } + /////////////////////////////////////////////////////////////////// +} //namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h new file mode 100644 index 000000000..6ac18d31e --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include +#include + +#include + +#include +#include "OpenXRInteractionProfilesProviderInterface.h" + + +namespace OpenXRVk +{ + struct OpenXRPath + { + AZStd::string m_displayName; + //! Although this path is relative, + //! it should start with "/". + //! Examples: + //! 1- For a User Path this string would look like this: + //! "/user/hand/left" + //! 2- For a Component Path this string would look like this: + //! "/input/select/click" + AZStd::string m_xrRelativePath; + }; + + struct OpenXRComponentPath : public OpenXRPath + { + XrActionType m_actionType; + }; + + //! This system component provides data that can be used to pick xrActions + //! that will used at runtime for any given application. + class OpenXRInteractionProfilesProviderSystemComponent final + : public AZ::Component + , private AzFramework::AssetCatalogEventBus::Handler + , private AZ::Data::AssetBus::Handler + , public OpenXRInteractionProfilesProviderInterface::Registrar + { + public: + AZ_COMPONENT(OpenXRInteractionProfilesProviderSystemComponent, "{123EDAF5-416B-4AEF-BEEC-03A8A8C71643}"); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + static void Reflect(AZ::ReflectContext* context); + + OpenXRInteractionProfilesProviderSystemComponent() = default; + ~OpenXRInteractionProfilesProviderSystemComponent() = default; + + ////////////////////////////////////////////////////////////////////////// + // Component + void Activate() override; + void Deactivate() override; + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + // AssetCatalogEventBus + void OnCatalogLoaded(const char* /*catalogFile*/) override; + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + // AZ::Data::AssetBus::Handler overrides + void OnAssetReady(AZ::Data::Asset asset) override; + void OnAssetError(AZ::Data::Asset asset) override; + ////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + // OpenXRInteractionProfileBus::Handler overrides + const AZStd::vector& GetInteractionProfileNames() const override; + const OpenXRInteractionProfileDescriptor* GetInteractionProfileDescriptor(const AZStd::string profileName) const override; + + // AZStd::string GetName() const override; + // AZStd::vector GetUserPaths() const override; + // AZStd::string GetUserTopPath(const AZStd::string& userPathName) const override; + // AZStd::vector GetComponentPaths(const AZStd::string& userPath) const override; + // OpenXRInteractionProfile::ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const override; + // AZStd::string GetInteractionProviderPath() const override; + + /////////////////////////////////////////////////////////////////// + + private: + + AZStd::string GetDefaultInteractionProfilesAssetPath(); + + static constexpr char LogName[] = "OpenXRInteractionProfilesProviderSystemComponent"; + + AZ::Data::Asset m_interactionProfilesAsset; + + }; +}//namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp index efca6ff54..4aa3c9c14 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp @@ -7,7 +7,7 @@ */ #include "InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h" -#include "OpenXRActionSetDescriptor.h" +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp index 4f0ed3886..c09f4e495 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp @@ -10,7 +10,7 @@ #include #include -#include "OpenXRActionSetsAsset.h" +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index 8ef44cb3c..71dd2828c 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -11,7 +11,7 @@ #include #include "OpenXRActionSetsAsset.h" -#include +//#include #include #include #include "OpenXRActionsManager.h" diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index 0a818ef7d..92d8b7edf 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -17,7 +17,7 @@ #include -#include "OpenXRActionsBindingAsset.h" +#include "OpenXRActionSetsAsset.h" namespace OpenXRVk { @@ -88,9 +88,10 @@ namespace OpenXRVk //! @param actionSet The user configured data for the ActionSet. //! @param suggestedBindingsPerProfile In this dictionary we will collect all the action bindings //! for each interaction profile. - bool InitActionSetInternal(const OpenXRActionSet& actionSet, - SuggestedBindingsPerProfile& suggestedBindingsPerProfile - ); + + //bool InitActionSetInternal(const OpenXRActionSet& actionSet, + // SuggestedBindingsPerProfile& suggestedBindingsPerProfile + // ); struct ActionInfo { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp index 542c1096a..f73f7e845 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp @@ -11,7 +11,7 @@ #include #include -#include "InteractionProfiles/KHRSimpleProfileSystemComponent.h" +#include "InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h" namespace OpenXRVk { @@ -27,9 +27,10 @@ namespace OpenXRVk : AZ::Module() { m_descriptors.insert(m_descriptors.end(), { + OpenXRInteractionProfilesProviderSystemComponent::CreateDescriptor(), SystemComponent::CreateDescriptor(), XRCameraMovementComponent::CreateDescriptor(), - KHRSimpleProfileSystemComponent::CreateDescriptor(), + //KHRSimpleProfileSystemComponent::CreateDescriptor(), }); } @@ -37,8 +38,8 @@ namespace OpenXRVk { return { + azrtti_typeid(), azrtti_typeid(), - azrtti_typeid(), }; } }; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index bb464e59b..dae5e04fa 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -21,8 +21,8 @@ #include #include -#include "OpenXRActionsManager.h" -#include "OpenXRVisualizedSpacesManager.h" +//#include "OpenXRActionsManager.h" +//#include "OpenXRVisualizedSpacesManager.h" namespace OpenXRVk { @@ -54,13 +54,13 @@ namespace OpenXRVk XrResult result = xrCreateSession(m_xrInstance, &createInfo, &m_session); ASSERT_IF_UNSUCCESSFUL(result); - m_visualizedSpacesMgr = AZStd::make_unique(); - bool success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); - AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); - - m_actionsMgr = AZStd::make_unique(); - success = m_actionsMgr->Init(m_xrInstance, m_session); - AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the actions manager"); + // m_visualizedSpacesMgr = AZStd::make_unique(); + // bool success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); + // AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); + // + // m_actionsMgr = AZStd::make_unique(); + // success = m_actionsMgr->Init(m_xrInstance, m_session); + // AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the actions manager"); LogReferenceSpaces(); @@ -127,7 +127,7 @@ namespace OpenXRVk // is not wearing the headset. Each time the proximity sensor is disabled or the user // decides to wear the headset, the XrSpaces need to be recreated, otherwise their // poses would be corrupted. - m_visualizedSpacesMgr->ResetSpaces(); + //m_visualizedSpacesMgr->ResetSpaces(); break; } case XR_SESSION_STATE_STOPPING: @@ -215,7 +215,7 @@ namespace OpenXRVk { // GALIB FIXME! AZ_Printf("GALIB", "OpenXRVkSession FIXME XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED\n"); - m_actionsMgr->LogCurrentInteractionProfile(); + // m_actionsMgr->LogCurrentInteractionProfile(); //if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled) @@ -401,12 +401,15 @@ namespace OpenXRVk const AZStd::vector& Session::GetXrViews() const { - return m_visualizedSpacesMgr->GetXrViews(); + static AZStd::vector ret; + return ret; + //return m_visualizedSpacesMgr->GetXrViews(); } XrSpace Session::GetViewSpaceXrSpace() const { - return m_visualizedSpacesMgr->GetViewSpaceXrSpace(); + return XR_NULL_HANDLE; + //return m_visualizedSpacesMgr->GetViewSpaceXrSpace(); } bool Session::IsSessionRunning() const @@ -442,20 +445,20 @@ namespace OpenXRVk return static_cast(GetInput()); } - void Session::OnBeginFrame(XrTime predictedDisplayTime) - { - if (IsSessionFocused()) - { - // Syncing actions only works if the session is in focused state - m_actionsMgr->SyncActions(predictedDisplayTime); - } - m_visualizedSpacesMgr->SyncViews(predictedDisplayTime); - - //Notify the rest of the engine. - AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, - m_visualizedSpacesMgr->GetViewSpacePose(), - m_visualizedSpacesMgr->GetViewPoses()); - - //GetNativeInput()->UpdateXrSpaceLocations(device, predictedDisplayTime, xrViews); + void Session::OnBeginFrame([[maybe_unused]] XrTime predictedDisplayTime) + { + // if (IsSessionFocused()) + // { + // // Syncing actions only works if the session is in focused state + // m_actionsMgr->SyncActions(predictedDisplayTime); + // } + // m_visualizedSpacesMgr->SyncViews(predictedDisplayTime); + // + // //Notify the rest of the engine. + // AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, + // m_visualizedSpacesMgr->GetViewSpacePose(), + // m_visualizedSpacesMgr->GetViewPoses()); + // + // //GetNativeInput()->UpdateXrSpaceLocations(device, predictedDisplayTime, xrViews); } } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index f41ead4dd..fb14ca7ef 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -37,9 +37,9 @@ namespace OpenXRVk ->Version(1); } - OpenXRActionBindingsAsset::Reflect(context); AzFramework::InputDeviceXRController::Reflect(context); OpenXRInteractionProfilesAsset::Reflect(context); + OpenXRActionSetsAsset::Reflect(context); } XR::Ptr SystemComponent::CreateInstance() @@ -84,8 +84,8 @@ namespace OpenXRVk void SystemComponent::Activate() { - m_actionsBindingAssetHandler = AZStd::make_unique>(OpenXRActionBindingsAsset::s_assetTypeName, "Other", OpenXRActionBindingsAsset::s_assetExtension); - m_actionsBindingAssetHandler->Register(); + m_actionSetsAssetHandler = AZStd::make_unique>(OpenXRActionSetsAsset::s_assetTypeName, "Other", OpenXRActionSetsAsset::s_assetExtension); + m_actionSetsAssetHandler->Register(); m_interactionProfilesAssetHandler = AZStd::make_unique(); m_interactionProfilesAssetHandler->Register(); @@ -119,7 +119,7 @@ namespace OpenXRVk m_instance = nullptr; } - m_actionsBindingAssetHandler->Unregister(); + m_actionSetsAssetHandler->Unregister(); m_interactionProfilesAssetHandler->Unregister(); } diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index 8a54ab605..e705494d4 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -19,6 +19,10 @@ set(FILES Include/OpenXRVk/OpenXRVkUtils.h Include/OpenXRVk/OpenXRActionsInterface.h Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h + Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h + Include/OpenXRVk/OpenXRInteractionProfilesAsset.h + Include/OpenXRVk/OpenXRActionSetDescriptor.h + Include/OpenXRVk/OpenXRActionSetsAsset.h Source/InputDeviceXRController.cpp Source/OpenXRVkCommon.h Source/OpenXRVkDevice.cpp @@ -33,18 +37,14 @@ set(FILES Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp - Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.h Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp - Source/InteractionProfiles/OpenXRInteractionProfilesAsset.h Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h Source/OpenXRActionSetsAsset.cpp - Source/OpenXRActionSetsAsset.h Source/OpenXRActionSetDescriptor.cpp - Source/OpenXRActionSetDescriptor.h Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h - Source/OpenXRActionsManager.cpp - Source/OpenXRActionsManager.h - Source/OpenXRVisualizedSpacesManager.cpp - Source/OpenXRVisualizedSpacesManager.h +# Source/OpenXRActionsManager.cpp +# Source/OpenXRActionsManager.h +# Source/OpenXRVisualizedSpacesManager.cpp +# Source/OpenXRVisualizedSpacesManager.h ) From f7ed8d5e4c07020a9f981e592297173797cf6d33 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:37:43 -0600 Subject: [PATCH 16/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRInteractionProfileDescriptor.h | 3 + .../OpenXRVk/OpenXRInteractionProfilesAsset.h | 7 + .../OpenXRVisualizedSpacesInterface.h | 6 +- .../Code/Include/OpenXRVk/OpenXRVkSession.h | 4 +- .../OpenXRInteractionProfileDescriptor.cpp | 41 +++ .../OpenXRInteractionProfilesAsset.cpp | 19 + .../Code/Source/OpenXRActionsManager.cpp | 340 ++++++++++-------- .../Code/Source/OpenXRActionsManager.h | 25 +- .../Code/Source/OpenXRBehaviorReflection.cpp | 200 +++++++++++ .../Code/Source/OpenXRBehaviorReflection.h | 20 ++ .../Source/OpenXRVisualizedSpacesManager.cpp | 18 +- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 53 ++- .../Code/Source/OpenXRVkSystemComponent.cpp | 7 + .../Code/openxrvk_private_common_files.cmake | 10 +- 14 files changed, 561 insertions(+), 192 deletions(-) create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.cpp create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.h diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h index ee04c83f7..e21f4a2b0 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h @@ -59,6 +59,7 @@ namespace OpenXRVk virtual ~OpenXRInteractionUserPathDescriptor() = default; static void Reflect(AZ::ReflectContext* reflection); + const OpenXRInteractionComponentPathDescriptor* GetComponentPathDescriptor(const AZStd::string& componentPathName) const; //! A user friendly name. AZStd::string m_name; @@ -88,6 +89,8 @@ namespace OpenXRVk AZ::Outcome Validate() const; const OpenXRInteractionUserPathDescriptor* GetUserPathDescriptor(const AZStd::string& userPathName) const; + const OpenXRInteractionComponentPathDescriptor* GetCommonComponentPathDescriptor(const AZStd::string& componentPathName) const; + AZStd::string GetComponentAbsolutePath(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, const AZStd::string& componentPathName) const; //! Unique name across all OpenXRInteractionProfileDescriptor. //! It serves also as user friendly display name, and because diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h index 0a0445732..4bf5e2f93 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h @@ -39,6 +39,13 @@ namespace OpenXRVk static constexpr char s_assetTypeName[] = "OpenXR Interaction Profiles"; static constexpr char s_assetExtension[] = "xrprofiles"; + //! There's only one Interaction Profile Asset that is active and defined upon engine + //! initialization. The developer can also customize which one to use via a registry property. + //! There's a default path in case the registry property is not defined by the developer. + static AZStd::string GetInteractionProfilesAssetPath(); + + const OpenXRInteractionProfileDescriptor* GetInteractionProfileDescriptor(const AZStd::string& profileName) const; + AZStd::vector m_interactionProfileDescriptors; }; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h index ccbd50828..302a905ee 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h @@ -37,9 +37,9 @@ namespace OpenXRVk static constexpr ReferenceSpaceId ReferenceSpaceIdLocal = 2; static constexpr ReferenceSpaceId ReferenceSpaceIdStage = 3; // With these names you can refer to the default system spaces. - static constexpr char ReferenceSpaceViewName[] = "View"; // Typically represents the user's head centroid. - static constexpr char ReferenceSpaceLocalName[] = "Local"; - static constexpr char ReferenceSpaceStageName[] = "Stage"; + static constexpr char ReferenceSpaceNameView[] = "View"; // Typically represents the user's head centroid. + static constexpr char ReferenceSpaceNameLocal[] = "Local"; + static constexpr char ReferenceSpaceNameStage[] = "Stage"; virtual AZStd::vector GetVisualizedSpaceNames() const = 0; virtual AZ::Outcome AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) = 0; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h index 7c64f4f88..5b260ce19 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h @@ -98,8 +98,8 @@ namespace OpenXRVk XrInstance m_xrInstance = XR_NULL_HANDLE; XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR }; - //AZStd::unique_ptr m_actionsMgr; - //AZStd::unique_ptr m_visualizedSpacesMgr; + AZStd::unique_ptr m_visualizedSpacesMgr; + AZStd::unique_ptr m_actionsMgr; // Application defined base space that will used to calculate // the relative pose of all other spaces. diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp index 5859571b6..3883a531c 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp @@ -129,6 +129,18 @@ namespace OpenXRVk return m_name.empty() ? "" : m_name; } + const OpenXRInteractionComponentPathDescriptor* OpenXRInteractionUserPathDescriptor::GetComponentPathDescriptor(const AZStd::string& componentPathName) const + { + for (const auto& componentPathDescriptor : m_componentPathDescriptors) + { + if (componentPathDescriptor.m_name == componentPathName) + { + return &componentPathDescriptor; + } + } + return nullptr; + } + /// OpenXRInteractionUserPathDescriptor /////////////////////////////////////////////////////////// @@ -209,6 +221,35 @@ namespace OpenXRVk } return nullptr; } + + const OpenXRInteractionComponentPathDescriptor* OpenXRInteractionProfileDescriptor::GetCommonComponentPathDescriptor(const AZStd::string& componentPathName) const + { + for (const auto& componentPathDescriptor : m_commonComponentPathDescriptors) + { + if (componentPathDescriptor.m_name == componentPathName) + { + return &componentPathDescriptor; + } + } + return nullptr; + } + + AZStd::string OpenXRInteractionProfileDescriptor::GetComponentAbsolutePath(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, + const AZStd::string& componentPathName) const + { + // First check if the user path owns the component path, if not, search in the common components list. + auto componentPathDescriptor = userPathDescriptor.GetComponentPathDescriptor(componentPathName); + if (!componentPathDescriptor) + { + // Look in common paths + componentPathDescriptor = GetCommonComponentPathDescriptor(componentPathName); + if (!componentPathDescriptor) + { + return {}; + } + } + return userPathDescriptor.m_path + componentPathDescriptor->m_path; + } /// OpenXRInteractionProfileDescriptor /////////////////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp index 8f98060c2..6ca18a266 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp @@ -12,6 +12,12 @@ namespace OpenXRVk { /////////////////////////////////////////////////////////// /// OpenXRInteractionProfilesAsset + /*static*/ AZStd::string OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath() + { + //FIXME! GALIB + return "system.xrprofiles"; + } + void OpenXRInteractionProfilesAsset::Reflect(AZ::ReflectContext* context) { OpenXRInteractionProfileDescriptor::Reflect(context); @@ -37,6 +43,19 @@ namespace OpenXRVk } } } + + const OpenXRInteractionProfileDescriptor* OpenXRInteractionProfilesAsset::GetInteractionProfileDescriptor(const AZStd::string& profileName) const + { + for (const auto& profileDescriptor : m_interactionProfileDescriptors) + { + if (profileName == profileDescriptor.m_uniqueName) + { + return &profileDescriptor; + } + } + return nullptr; + } + /// OpenXRInteractionProfilesAsset /////////////////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index 71dd2828c..aacb0cce0 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -10,7 +10,6 @@ #include -#include "OpenXRActionSetsAsset.h" //#include #include #include @@ -18,37 +17,96 @@ namespace OpenXRVk { + // The action type of an action will always be the action type of the first action path descriptor. + static XrActionType GetActionTypeFromActionDescriptor(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRActionDescriptor& actionDescriptor) + { + if (actionDescriptor.m_actionPathDescriptors.empty()) + { + AZ_Error(ActionsManager::LogName, false, "Need at least one action path descriptor for action with name [%s].", actionDescriptor.m_name.c_str()); + return XR_ACTION_TYPE_MAX_ENUM; + } + + const auto& actionPathDescriptor = actionDescriptor.m_actionPathDescriptors[0]; + + auto interactionProfileDescriptor = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); + if (!interactionProfileDescriptor) + { + AZ_Error(ActionsManager::LogName, false, "Couldn't find interaction profile named [%s].", actionPathDescriptor.m_interactionProfileName.c_str()); + return XR_ACTION_TYPE_MAX_ENUM; + } + + auto userPathDescriptor = interactionProfileDescriptor->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); + if (!userPathDescriptor) + { + AZ_Error(ActionsManager::LogName, false, "Couldn't find user path descriptor with name [%s].", actionPathDescriptor.m_userPathName.c_str()); + return XR_ACTION_TYPE_MAX_ENUM; + } + + // See if the component is owned by the user path. + auto componentPathDescriptor = userPathDescriptor->GetComponentPathDescriptor(actionPathDescriptor.m_componentPathName); + if (!componentPathDescriptor) + { + componentPathDescriptor = interactionProfileDescriptor->GetCommonComponentPathDescriptor(actionPathDescriptor.m_componentPathName); + if (!componentPathDescriptor) + { + AZ_Error(ActionsManager::LogName, false, "Couldn't find component path descriptor for component path with name [%s].", actionPathDescriptor.m_componentPathName.c_str()); + return XR_ACTION_TYPE_MAX_ENUM; + } + } + + return componentPathDescriptor->GetXrActionType(); + } + + bool ActionsManager::Init(XrInstance xrInstance, XrSession xrSession) { m_xrInstance = xrInstance; m_xrSession = xrSession; - auto outcome = SetBaseVisualizedSpaceForPoseActions(IOpenXRVisualizedSpaces::ReferenceSpaceViewName); + auto outcome = SetBaseVisualizedSpaceForPoseActions(IOpenXRVisualizedSpaces::ReferenceSpaceNameView); if (!outcome.IsSuccess()) { const auto outcomeMsg = outcome.TakeError(); auto errorMsg = AZStd::string::format("Failed to set [%s] as the default base visualized space. Reason:\n%s.", - IOpenXRVisualizedSpaces::ReferenceSpaceViewName, outcomeMsg.c_str()); + IOpenXRVisualizedSpaces::ReferenceSpaceNameView, outcomeMsg.c_str()); AZ_Assert(false, "%s", errorMsg.c_str()); AZ_Error(LogName, false, "%s", errorMsg.c_str()); return false; } + // There are two critical assets that need to be loaded. + // The first asset defines the list of interaction profiles supported by the current version + // of the OpenXR Gem. + const auto interactionProfilesAssetPath = OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath(); + if (interactionProfilesAssetPath.empty()) + { + AZ_Warning(LogName, false, "No interaction profile asset has been defined. This application will run without user interaction support."); + return true; + } + const auto interactionProfilesAsset = AZ::RPI::AssetUtils::LoadCriticalAsset(interactionProfilesAssetPath); + if (!interactionProfilesAsset.IsReady()) + { + AZ_Warning(LogName, false, "The system interaction profiles asset [%s] is not ready. This application will run without user interaction support.", + interactionProfilesAssetPath.c_str()); + return true; + } + // OpenXR only allows to define ActionSets during session creation. // From the point of view of O3DE, the developer defines action sets // in an asset of type OpenXRActionBindingsAsset. // The default source path for said asset is "@project@/openxr.xractions". - const auto actionsBindingAsset = AZ::RPI::AssetUtils::LoadCriticalAsset({ DefaultActionsAssetPath }); - if (!actionsBindingAsset.IsReady()) + m_actionSetAsset = AZ::RPI::AssetUtils::LoadCriticalAsset({ DefaultActionsAssetPath }); + if (!m_actionSetAsset.IsReady()) { - AZ_Printf(LogName, "This application won't support user interactions. Default action bindings asset [%s] not found.\n", DefaultActionsAssetPath); + AZ_Warning(LogName, false, "Action Sets asset [%s] not found. This application will run without user interaction support.", DefaultActionsAssetPath); return true; } SuggestedBindingsPerProfile suggestedBindingsPerProfile; - for (const auto& actionSet : actionsBindingAsset->m_actionSets) + for (const auto& actionSetDescriptor : m_actionSetAsset->m_actionSetDescriptors) { - if (!InitActionSetInternal(actionSet, suggestedBindingsPerProfile)) + if (!InitActionSet(*interactionProfilesAsset, actionSetDescriptor, suggestedBindingsPerProfile)) { return false; } @@ -135,40 +193,41 @@ namespace OpenXRVk - bool ActionsManager::InitActionSetInternal(const OpenXRActionSet& actionSet, + bool ActionsManager::InitActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRActionSetDescriptor& actionSetDescriptor, SuggestedBindingsPerProfile& suggestedBindingsPerProfile) { // Create an action set. XrActionSetCreateInfo actionSetCreateInfo{}; actionSetCreateInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO; - azstrcpy(actionSetCreateInfo.actionSetName, sizeof(actionSetCreateInfo.actionSetName), actionSet.m_name.c_str()); - const char* localizedNameCStr = actionSet.m_name.c_str(); - if (!actionSet.m_localizedName.empty()) + azstrcpy(actionSetCreateInfo.actionSetName, sizeof(actionSetCreateInfo.actionSetName), actionSetDescriptor.m_name.c_str()); + const char* localizedNameCStr = actionSetDescriptor.m_name.c_str(); + if (!actionSetDescriptor.m_localizedName.empty()) { - localizedNameCStr = actionSet.m_localizedName.c_str(); + localizedNameCStr = actionSetDescriptor.m_localizedName.c_str(); } azstrcpy(actionSetCreateInfo.localizedActionSetName, sizeof(actionSetCreateInfo.localizedActionSetName), localizedNameCStr); - actionSetCreateInfo.priority = actionSet.m_priority; + actionSetCreateInfo.priority = actionSetDescriptor.m_priority; { ActionSetInfo newActionSetInfo; - newActionSetInfo.m_name = actionSet.m_name; + newActionSetInfo.m_name = actionSetDescriptor.m_name; XrResult result = xrCreateActionSet(m_xrInstance, &actionSetCreateInfo, &newActionSetInfo.m_xrActionSet); if (IsError(result)) { - PrintXrError(LogName, result, "Failed to instantiate actionSet named [%s].", actionSet.m_name.c_str()); + PrintXrError(LogName, result, "Failed to instantiate actionSet named [%s].", actionSetDescriptor.m_name.c_str()); return false; } m_actionSets.emplace_back(AZStd::move(newActionSetInfo)); } ActionSetInfo& newActionSetInfo = m_actionSets.back(); - for (const auto& action : actionSet.m_actions) + for (const auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors) { - if (!InitActionBindingsInternal(newActionSetInfo, action, suggestedBindingsPerProfile)) + if (!AddActionToActionSet(interactionProfilesAsset, newActionSetInfo, actionDescriptor, suggestedBindingsPerProfile)) { - AZ_Error(LogName, false, "Failed to created action named [%s] under actionSet named [%s].", - action.m_name.c_str(), actionSet.m_name.c_str()); + AZ_Error(LogName, false, "Failed to initialize action bindings for action named [%s] under actionSet named [%s].", + actionDescriptor.m_name.c_str(), actionSetDescriptor.m_name.c_str()); return false; } } @@ -176,28 +235,63 @@ namespace OpenXRVk return true; } - static XrActionType GetActionTypeFromOpenXRActionPath(const OpenXRActionPath& actionPath) + bool ActionsManager::AddActionToActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, + ActionSetInfo& actionSetInfo, + const OpenXRActionDescriptor& actionDescriptor, + SuggestedBindingsPerProfile& suggestedBindingsPerProfile) { - auto interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(actionPath.m_interactionProfile); - if (!interactionProviderIface) + // One OpenXRAction object will become one XrAction. + // An OpenXRAction contains a list of OpenXRActionPath that need to be bound. + // The action type for each XrAction will be the same and it will be determined by + // the action type of the first action in the list. + AZ_Assert(!actionDescriptor.m_actionPathDescriptors.empty(), "An action descriptor must contain at least one action path descriptor."); + XrActionType actionType = GetActionTypeFromActionDescriptor(interactionProfilesAsset, actionDescriptor); + + XrSpace newXrActionSpace = XR_NULL_HANDLE; // Optional. + XrAction newXrAction = CreateXrActionAndXrSpace(actionSetInfo, actionDescriptor, actionType, newXrActionSpace); + if (newXrAction == XR_NULL_HANDLE) { - AZ_Error(ActionsManager::LogName, false, "Couldn't find interaction data provider with id [%s].", actionPath.m_interactionProfile.c_str()); - return XR_ACTION_TYPE_MAX_ENUM; + return false; + } + + // For each actionPath in the list, create the XrPath and its binding. + const auto additionalBindingsCount = AppendActionBindings(interactionProfilesAsset, actionDescriptor, newXrAction, suggestedBindingsPerProfile); + if (additionalBindingsCount < 1) + { + // This action has no bindings. Don't add it to the active actions list. + AZ_Warning(LogName, false, "The action [%s] had no bindings!.\n", actionDescriptor.m_name.c_str()); + if (newXrActionSpace != XR_NULL_HANDLE) + { + xrDestroySpace(newXrActionSpace); + } + xrDestroyAction(newXrAction); + return true; } - const auto actionPathInfo = interactionProviderIface->GetActionPathInfo(actionPath.m_userPath, actionPath.m_componentPath); - return actionPathInfo.m_actionType; + + m_actions.push_back({}); + auto& newActionInfo = m_actions.back(); + newActionInfo.m_name = actionDescriptor.m_name; + newActionInfo.m_actionType = actionType; + newActionInfo.m_xrAction = newXrAction; + newActionInfo.m_xrSpace = newXrActionSpace; + + uint16_t newActionIndex = aznumeric_cast(m_actions.size() - 1); + actionSetInfo.m_actions.emplace(actionDescriptor.m_name, IOpenXRActions::ActionHandle(newActionIndex)); + + return true; } + XrAction ActionsManager::CreateXrActionAndXrSpace(const ActionSetInfo& actionSetInfo, - const OpenXRAction& action, const XrActionType actionType, XrSpace& newXrActionSpace) const + const OpenXRActionDescriptor& actionDescriptor, const XrActionType actionType, XrSpace& newXrActionSpace) const { XrActionCreateInfo actionCreateInfo{ XR_TYPE_ACTION_CREATE_INFO }; actionCreateInfo.actionType = actionType; - azstrcpy(actionCreateInfo.actionName, sizeof(actionCreateInfo.actionName), action.m_name.c_str()); - const char* localizedNameCStr = action.m_name.c_str(); - if (!action.m_localizedName.empty()) + azstrcpy(actionCreateInfo.actionName, sizeof(actionCreateInfo.actionName), actionDescriptor.m_name.c_str()); + const char* localizedNameCStr = actionDescriptor.m_name.c_str(); + if (!actionDescriptor.m_localizedName.empty()) { - localizedNameCStr = action.m_localizedName.c_str(); + localizedNameCStr = actionDescriptor.m_localizedName.c_str(); } azstrcpy(actionCreateInfo.localizedActionName, sizeof(actionCreateInfo.localizedActionName), localizedNameCStr); actionCreateInfo.countSubactionPaths = 0; // Subactions are not supported. @@ -207,7 +301,7 @@ namespace OpenXRVk XrResult result = xrCreateAction(actionSetInfo.m_xrActionSet, &actionCreateInfo, &newXrAction); if (IsError(result)) { - PrintXrError(ActionsManager::LogName, result, "Failed to create action named %s.\n", action.m_name.c_str()); + PrintXrError(LogName, result, "Failed to create XrAction named %s.\n", actionDescriptor.m_name.c_str()); return XR_NULL_HANDLE; } @@ -222,7 +316,7 @@ namespace OpenXRVk if (IsError(result)) { xrDestroyAction(newXrAction); - PrintXrError(ActionsManager::LogName, result, "Failed to create XrSpace for action named %s.\n", action.m_name.c_str()); + PrintXrError(LogName, result, "Failed to create XrSpace for action named %s.\n", actionDescriptor.m_name.c_str()); return XR_NULL_HANDLE; } } @@ -230,57 +324,63 @@ namespace OpenXRVk return newXrAction; } - uint32_t ActionsManager::AppendActionBindings(const OpenXRAction& action, - XrAction newXrAction, + uint32_t ActionsManager::AppendActionBindings(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRActionDescriptor& actionDescriptor, XrAction xrAction, SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const { uint32_t additionalBindingsCount = 0; - for (const auto& actionPath : action.m_actionPaths) + for (const auto& actionPathDescriptor : actionDescriptor.m_actionPathDescriptors) { - auto interactionProviderIface = OpenXRInteractionProfileBus::FindFirstHandler(actionPath.m_interactionProfile); - if (!interactionProviderIface) + auto interactionProfileDescriptor = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); + if (!interactionProfileDescriptor) { - AZ_Error(LogName, false, "Couldn't find interaction data provider with id [%s].", actionPath.m_interactionProfile.c_str()) - return false; + AZ_Error(LogName, false, "Couldn't find interaction profile descriptor with name [%s].", actionPathDescriptor.m_interactionProfileName.c_str()); + return false; } - const auto pathInfo = interactionProviderIface->GetActionPathInfo(actionPath.m_userPath, actionPath.m_componentPath); - if (pathInfo.m_absolutePath.empty()) + auto userPathDescriptor = interactionProfileDescriptor->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); + if (!userPathDescriptor) { - AZ_Warning(LogName, false, "Failed to retrieve action path info for profile [%s], user path [%s], component path [%s].\n", - actionPath.m_interactionProfile.c_str(), actionPath.m_userPath.c_str(), actionPath.m_componentPath.c_str()); - continue; + AZ_Error(LogName, false, "Couldn't find user path descriptor with name [%s].", actionPathDescriptor.m_userPathName.c_str()); + return false; + } + + const auto absoluteComponentPath = interactionProfileDescriptor->GetComponentAbsolutePath(*userPathDescriptor, actionPathDescriptor.m_componentPathName); + if (absoluteComponentPath.empty()) + { + AZ_Error(LogName, false, "Failed to retrieve the absolute action path for profile [%s], user path [%s], component path [%s].\n", + actionPathDescriptor.m_interactionProfileName.c_str(), actionPathDescriptor.m_userPathName.c_str(), actionPathDescriptor.m_componentPathName.c_str()); + return false; } XrPath xrBindingPath; - XrResult result = xrStringToPath(m_xrInstance, pathInfo.m_absolutePath.c_str(), &xrBindingPath); + XrResult result = xrStringToPath(m_xrInstance, absoluteComponentPath.c_str(), &xrBindingPath); if (IsError(result)) { PrintXrError(LogName, result, "Failed to create XrPath for action with profile [%s], absolute path [%s].\n", - actionPath.m_interactionProfile.c_str(), pathInfo.m_absolutePath.c_str()); + actionPathDescriptor.m_interactionProfileName.c_str(), absoluteComponentPath.c_str()); continue; } // If the interaction profile is not in the dictionary, then add it. - auto profilePathStr = interactionProviderIface->GetInteractionProviderPath(); - if (!suggestedBindingsPerProfile.contains(profilePathStr)) + if (!suggestedBindingsPerProfile.contains(interactionProfileDescriptor->m_uniqueName)) { XrPath profileXrPath; - result = xrStringToPath(m_xrInstance, profilePathStr.c_str(), &profileXrPath); + result = xrStringToPath(m_xrInstance, interactionProfileDescriptor->m_path.c_str(), &profileXrPath); if (IsError(result)) { - PrintXrError(LogName, result, "Failed to get profile [%s] XrPath while working on action [%s] and the action path [%s]", - profilePathStr.c_str(), action.m_name.c_str(), actionPath.GetEditorText().c_str()); + PrintXrError(LogName, result, "Failed to get profile [%s] XrPath while working on action [%s]", + interactionProfileDescriptor->m_path.c_str(), actionDescriptor.m_name.c_str()); continue; } - suggestedBindingsPerProfile.emplace(profilePathStr, SuggestedBindings{}); - SuggestedBindings& newProfileBindings = suggestedBindingsPerProfile.at(profilePathStr); + suggestedBindingsPerProfile.emplace(interactionProfileDescriptor->m_uniqueName, SuggestedBindings{}); + SuggestedBindings& newProfileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_uniqueName); newProfileBindings.m_profileXrPath = profileXrPath; } - SuggestedBindings& profileBindings = suggestedBindingsPerProfile.at(profilePathStr); + SuggestedBindings& profileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_uniqueName); XrActionSuggestedBinding binding; - binding.action = newXrAction; + binding.action = xrAction; binding.binding = xrBindingPath; profileBindings.m_suggestedBindingsList.push_back(binding); additionalBindingsCount++; @@ -289,94 +389,52 @@ namespace OpenXRVk return additionalBindingsCount; } - bool ActionsManager::InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, - SuggestedBindingsPerProfile& suggestedBindingsPerProfile) - { - // One OpenXRAction object will become one XrAction. - // An OpenXRAction contains a list of OpenXRActionPath that need to be bound. - // The action type for each XrAction will be the same and it will be determined by - // the action type of the first action in the list. - AZ_Assert(!action.m_actionPaths.empty(), "OpenXR Actions list must contain at least one action."); - XrActionType firstActionType = GetActionTypeFromOpenXRActionPath(action.m_actionPaths[0]); - - XrSpace newXrActionSpace = XR_NULL_HANDLE; // Optional. - XrAction newXrAction = CreateXrActionAndXrSpace(actionSetInfo, action, firstActionType, newXrActionSpace); - if (newXrAction == XR_NULL_HANDLE) - { - return false; - } - - // For each actionPath in the list, create the XrPath and its binding. - const auto additionalBindingsCount = AppendActionBindings(action, newXrAction, suggestedBindingsPerProfile); - if (additionalBindingsCount < 1) - { - // This action has no bindings. Don't add it to the active actions list. - AZ_Warning(LogName, false, "The action [%s] had no bindings!.\n", action.m_name.c_str()); - if (newXrActionSpace != XR_NULL_HANDLE) - { - xrDestroySpace(newXrActionSpace); - } - xrDestroyAction(newXrAction); - return true; - } - - m_actions.push_back({}); - auto& newActionInfo = m_actions.back(); - newActionInfo.m_name = action.m_name; - newActionInfo.m_actionType = firstActionType; - newActionInfo.m_xrAction = newXrAction; - newActionInfo.m_xrSpace = newXrActionSpace; - uint16_t newActionIndex = aznumeric_cast(m_actions.size() - 1); - actionSetInfo.m_actions.emplace(action.m_name, IOpenXRActions::ActionHandle(newActionIndex)); - - return true; - } void ActionsManager::LogCurrentInteractionProfile() { - OpenXRInteractionProfileBus::EnumerateHandlers( - [this](OpenXRInteractionProfile* handler) -> bool - { - auto userPathStrs = handler->GetUserPaths(); - auto profileName = handler->GetName(); - AZ_Printf(LogName, "Visiting user paths for interaction profile [%s]\n", profileName.c_str()); - for (const auto& userPathStr : userPathStrs) - { - const auto topPathstr = handler->GetUserTopPath(userPathStr); - XrPath xrPath; - XrResult result = xrStringToPath(m_xrInstance, topPathstr.c_str(), &xrPath); - if (IsError(result)) - { - PrintXrError(LogName, result, "Failed to get xrPath for user top path [%s]", topPathstr.c_str()); - continue; - } - XrInteractionProfileState profileStateOut{ XR_TYPE_INTERACTION_PROFILE_STATE }; - result = xrGetCurrentInteractionProfile( - m_xrSession, xrPath, &profileStateOut); - if (IsError(result)) - { - PrintXrError(LogName, result, "Failed to get profile state for user top path [%s]", topPathstr.c_str()); - continue; - } - if (profileStateOut.interactionProfile == XR_NULL_PATH) - { - AZ_Printf(LogName, "Got an NULL Interaction Profile for [%s].\n", topPathstr.c_str()); - continue; - } - AZStd::string activeProfileName = ConvertXrPathToString(m_xrInstance, profileStateOut.interactionProfile); - if (activeProfileName.empty()) - { - PrintXrError(LogName, result, "Failed to convert Interaction Profile XrPath to string for user top path [%s]", topPathstr.c_str()); - continue; - } - else - { - AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", topPathstr.c_str(), activeProfileName.c_str()); - } - } - return true; - }); + // OpenXRInteractionProfileBus::EnumerateHandlers( + // [this](OpenXRInteractionProfile* handler) -> bool + // { + // auto userPathStrs = handler->GetUserPaths(); + // auto profileName = handler->GetName(); + // AZ_Printf(LogName, "Visiting user paths for interaction profile [%s]\n", profileName.c_str()); + // for (const auto& userPathStr : userPathStrs) + // { + // const auto topPathstr = handler->GetUserTopPath(userPathStr); + // XrPath xrPath; + // XrResult result = xrStringToPath(m_xrInstance, topPathstr.c_str(), &xrPath); + // if (IsError(result)) + // { + // PrintXrError(LogName, result, "Failed to get xrPath for user top path [%s]", topPathstr.c_str()); + // continue; + // } + // XrInteractionProfileState profileStateOut{ XR_TYPE_INTERACTION_PROFILE_STATE }; + // result = xrGetCurrentInteractionProfile( + // m_xrSession, xrPath, &profileStateOut); + // if (IsError(result)) + // { + // PrintXrError(LogName, result, "Failed to get profile state for user top path [%s]", topPathstr.c_str()); + // continue; + // } + // if (profileStateOut.interactionProfile == XR_NULL_PATH) + // { + // AZ_Printf(LogName, "Got an NULL Interaction Profile for [%s].\n", topPathstr.c_str()); + // continue; + // } + // AZStd::string activeProfileName = ConvertXrPathToString(m_xrInstance, profileStateOut.interactionProfile); + // if (activeProfileName.empty()) + // { + // PrintXrError(LogName, result, "Failed to convert Interaction Profile XrPath to string for user top path [%s]", topPathstr.c_str()); + // continue; + // } + // else + // { + // AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", topPathstr.c_str(), activeProfileName.c_str()); + // } + // } + // return true; + // }); } ///////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index 92d8b7edf..9f5c8ce72 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -16,8 +16,8 @@ #include #include - -#include "OpenXRActionSetsAsset.h" +#include +#include namespace OpenXRVk { @@ -111,20 +111,35 @@ namespace OpenXRVk AZStd::unordered_map m_actions; }; - bool InitActionBindingsInternal(ActionSetInfo& actionSetInfo, const OpenXRAction& action, + bool InitActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRActionSetDescriptor& actionSetDescriptor, + SuggestedBindingsPerProfile& suggestedBindingsPerProfileOut); + + bool AddActionToActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, + ActionSetInfo& actionSetInfo, + const OpenXRActionDescriptor& actionDescriptor, SuggestedBindingsPerProfile& suggestedBindingsPerProfile); XrAction CreateXrActionAndXrSpace(const ActionSetInfo& actionSetInfo, - const OpenXRAction& action, const XrActionType actionType, XrSpace& newXrActionSpace) const; + const OpenXRActionDescriptor& actionDescriptor, const XrActionType actionType, XrSpace& newXrActionSpace) const; - uint32_t AppendActionBindings(const OpenXRAction& action, XrAction newXrAction, + uint32_t AppendActionBindings(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRActionDescriptor& actionDescriptor, + XrAction xrAction, SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const; + + AZ::Outcome ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false); void RecreateXrActiveActionSets(); XrInstance m_xrInstance = XR_NULL_HANDLE; XrSession m_xrSession = XR_NULL_HANDLE; + + // Load synchronously as critical assets upon initilization. + AZ::Data::Asset m_interactionProfilesAsset; + AZ::Data::Asset m_actionSetAsset; + // Updated each time SyncActions is called. XrTime m_predictedDisplaytime; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.cpp b/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.cpp new file mode 100644 index 000000000..0685b8fe5 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include "OpenXRBehaviorReflection.h" + +// #include + +#include + +namespace OpenXRVk +{ + class OpenXRVisualizedSpaces + { + public: + AZ_TYPE_INFO(OpenXRVisualizedSpaces, "{A060D5C5-1514-421B-9AAA-1E276BA2E33E}"); + AZ_CLASS_ALLOCATOR(OpenXRVisualizedSpaces, AZ::SystemAllocator); + + static constexpr char LogName[] = "OpenXRVisualizedSpaces"; + + OpenXRVisualizedSpaces() = default; + ~OpenXRVisualizedSpaces() = default; + + static AZStd::vector GetVisualizedSpaceNames() + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + return {}; + } + return iface->GetVisualizedSpaceNames(); + } + + static AZ::Outcome AddVisualizedSpace(IOpenXRVisualizedSpaces::ReferenceSpaceId referenceSpaceType, + const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + ); + } + return iface->AddVisualizedSpace(referenceSpaceType, spaceName, poseInReferenceSpace); + } + + static AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + ); + } + return iface->RemoveVisualizedSpace(spaceName); + } + + static AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + ); + } + return iface->GetVisualizedSpacePose(spaceName, baseSpaceName); + } + + static AZ::Outcome SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + ); + } + return iface->SetBaseSpaceForViewSpacePose(spaceName); + } + + static const AZStd::string& GetBaseSpaceForViewSpacePose() + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + static const AZStd::string emptyStr; + return emptyStr; + } + return iface->GetBaseSpaceForViewSpacePose(); + } + + static const AZ::Transform& GetViewSpacePose() + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + return AZ::Transform::Identity(); + } + return iface->GetViewSpacePose(); + } + + static uint32_t GetViewCount() + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + return 0; + } + return iface->GetViewCount(); + } + + static const AZ::Transform& GetViewPose(uint32_t eyeIndex) + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + return AZ::Transform::Identity(); + } + return iface->GetViewPose(eyeIndex); + } + + //TODO: Serialize AZ::RPI::FovData + static const AZ::RPI::FovData& GetViewFovData(uint32_t eyeIndex) + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + static const AZ::RPI::FovData fovData{}; + return fovData; + } + return iface->GetViewFovData(eyeIndex); + } + + static const AZStd::vector& GetViewPoses() + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + static AZStd::vector EmptyList; + return EmptyList; + } + return iface->GetViewPoses(); + } + + static void ForceViewPosesCacheUpdate() + { + const auto iface = OpenXRVisualizedSpacesInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + return; + } + iface->ForceViewPosesCacheUpdate(); + } + + }; + + void OpenXRBehaviorReflect(AZ::BehaviorContext& context) + { + context.Class("OpenXRVisualizedSpaces") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "openxr") + ->Constant("ReferenceSpaceIdView", BehaviorConstant(IOpenXRVisualizedSpaces::ReferenceSpaceIdView)) + ->Constant("ReferenceSpaceIdLocal", BehaviorConstant(IOpenXRVisualizedSpaces::ReferenceSpaceIdLocal)) + ->Constant("ReferenceSpaceIdStage", BehaviorConstant(IOpenXRVisualizedSpaces::ReferenceSpaceIdStage)) + ->Constant("ReferenceSpaceNameView", BehaviorConstant(AZStd::string(IOpenXRVisualizedSpaces::ReferenceSpaceNameView))) + ->Constant("ReferenceSpaceNameLocal", BehaviorConstant(AZStd::string(IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal))) + ->Constant("ReferenceSpaceNameStage", BehaviorConstant(AZStd::string(IOpenXRVisualizedSpaces::ReferenceSpaceNameStage))) + ->Constant("LeftEyeViewId", BehaviorConstant(IOpenXRVisualizedSpaces::LeftEyeView)) + ->Constant("RightEyeViewId", BehaviorConstant(IOpenXRVisualizedSpaces::RightEyeView)) + ->Method("GetVisualizedSpaceNames", &OpenXRVisualizedSpaces::GetVisualizedSpaceNames) + ->Method("AddVisualizedSpace", &OpenXRVisualizedSpaces::AddVisualizedSpace) + ->Method("RemoveVisualizedSpace", &OpenXRVisualizedSpaces::RemoveVisualizedSpace) + ->Method("GetVisualizedSpacePose", &OpenXRVisualizedSpaces::GetVisualizedSpacePose) + ->Method("SetBaseSpaceForViewSpacePose", &OpenXRVisualizedSpaces::SetBaseSpaceForViewSpacePose) + ->Method("GetBaseSpaceForViewSpacePose", &OpenXRVisualizedSpaces::GetBaseSpaceForViewSpacePose) + ->Method("GetViewSpacePose", &OpenXRVisualizedSpaces::GetViewSpacePose) + ->Method("GetViewCount", &OpenXRVisualizedSpaces::GetViewCount) + ->Method("GetViewPose", &OpenXRVisualizedSpaces::GetViewPose) + //TODO: Serialize AZ::RPI::FovData + //->Method("GetViewFovData", &OpenXRVisualizedSpaces::GetViewFovData) + ->Method("GetViewPoses", &OpenXRVisualizedSpaces::GetViewPoses) + ->Method("ForceViewPosesCacheUpdate", &OpenXRVisualizedSpaces::ForceViewPosesCacheUpdate) + ; + } + +} diff --git a/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.h b/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.h new file mode 100644 index 000000000..fb69276ca --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ +#pragma once + +#include + +namespace OpenXRVk +{ + /** + * Reflects OpenXR related AZ::Interfaces as classes + * in the behavior context. + */ + void OpenXRBehaviorReflect(AZ::BehaviorContext& context); +} + diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp index 65ba52103..5b53f88b2 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp @@ -33,9 +33,9 @@ namespace OpenXRVk const auto identityTm = AZ::Transform::CreateIdentity(); AZStd::array, 3> ReferenceSpaces = { { - {IOpenXRVisualizedSpaces::ReferenceSpaceIdView, IOpenXRVisualizedSpaces::ReferenceSpaceViewName }, - {IOpenXRVisualizedSpaces::ReferenceSpaceIdLocal, IOpenXRVisualizedSpaces::ReferenceSpaceLocalName }, - {IOpenXRVisualizedSpaces::ReferenceSpaceIdStage, IOpenXRVisualizedSpaces::ReferenceSpaceStageName } + {IOpenXRVisualizedSpaces::ReferenceSpaceIdView, IOpenXRVisualizedSpaces::ReferenceSpaceNameView }, + {IOpenXRVisualizedSpaces::ReferenceSpaceIdLocal, IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal }, + {IOpenXRVisualizedSpaces::ReferenceSpaceIdStage, IOpenXRVisualizedSpaces::ReferenceSpaceNameStage } } }; for (const auto& refPair : ReferenceSpaces) @@ -50,10 +50,10 @@ namespace OpenXRVk } // Set the default base space for View Space pose calculation. - auto outcome = SetBaseSpaceForViewSpacePose({ IOpenXRVisualizedSpaces::ReferenceSpaceLocalName }); + auto outcome = SetBaseSpaceForViewSpacePose({ IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal }); AZ_Assert(outcome.IsSuccess(), "Failed to set the base space for View Space pose location"); - const auto& viewSpace = m_spaces.at(IOpenXRVisualizedSpaces::ReferenceSpaceViewName); + const auto& viewSpace = m_spaces.at(IOpenXRVisualizedSpaces::ReferenceSpaceNameView); m_viewSpace = &viewSpace; return true; } @@ -128,9 +128,9 @@ namespace OpenXRVk AZ::Outcome VisualizedSpacesManager::RemoveVisualizedSpace(const AZStd::string& spaceName) { static const AZStd::unordered_set defaultSystemSpaces { - {IOpenXRVisualizedSpaces::ReferenceSpaceViewName }, - {IOpenXRVisualizedSpaces::ReferenceSpaceLocalName}, - {IOpenXRVisualizedSpaces::ReferenceSpaceStageName} + {IOpenXRVisualizedSpaces::ReferenceSpaceNameView }, + {IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal}, + {IOpenXRVisualizedSpaces::ReferenceSpaceNameStage} }; if (defaultSystemSpaces.contains(spaceName)) { @@ -144,7 +144,7 @@ namespace OpenXRVk (m_baseSpaceForViewSpace->m_name == spaceName)) { return AZ::Failure(AZStd::string::format("Can not remove space [%s] because it is the base space to locate the [%s] space pose.", - spaceName.c_str(), IOpenXRVisualizedSpaces::ReferenceSpaceViewName)); + spaceName.c_str(), IOpenXRVisualizedSpaces::ReferenceSpaceNameView)); } auto itor = m_spaces.find(spaceName); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index dae5e04fa..caebfea1d 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -21,8 +21,9 @@ #include #include -//#include "OpenXRActionsManager.h" -//#include "OpenXRVisualizedSpacesManager.h" +#include "OpenXRVisualizedSpacesManager.h" +#include "OpenXRActionsManager.h" + namespace OpenXRVk { @@ -54,13 +55,13 @@ namespace OpenXRVk XrResult result = xrCreateSession(m_xrInstance, &createInfo, &m_session); ASSERT_IF_UNSUCCESSFUL(result); - // m_visualizedSpacesMgr = AZStd::make_unique(); - // bool success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); - // AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); - // - // m_actionsMgr = AZStd::make_unique(); - // success = m_actionsMgr->Init(m_xrInstance, m_session); - // AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the actions manager"); + m_visualizedSpacesMgr = AZStd::make_unique(); + bool success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); + AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); + + m_actionsMgr = AZStd::make_unique(); + success = m_actionsMgr->Init(m_xrInstance, m_session); + AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the actions manager"); LogReferenceSpaces(); @@ -127,7 +128,7 @@ namespace OpenXRVk // is not wearing the headset. Each time the proximity sensor is disabled or the user // decides to wear the headset, the XrSpaces need to be recreated, otherwise their // poses would be corrupted. - //m_visualizedSpacesMgr->ResetSpaces(); + m_visualizedSpacesMgr->ResetSpaces(); break; } case XR_SESSION_STATE_STOPPING: @@ -401,15 +402,12 @@ namespace OpenXRVk const AZStd::vector& Session::GetXrViews() const { - static AZStd::vector ret; - return ret; - //return m_visualizedSpacesMgr->GetXrViews(); + return m_visualizedSpacesMgr->GetXrViews(); } XrSpace Session::GetViewSpaceXrSpace() const { - return XR_NULL_HANDLE; - //return m_visualizedSpacesMgr->GetViewSpaceXrSpace(); + return m_visualizedSpacesMgr->GetViewSpaceXrSpace(); } bool Session::IsSessionRunning() const @@ -447,18 +445,17 @@ namespace OpenXRVk void Session::OnBeginFrame([[maybe_unused]] XrTime predictedDisplayTime) { - // if (IsSessionFocused()) - // { - // // Syncing actions only works if the session is in focused state - // m_actionsMgr->SyncActions(predictedDisplayTime); - // } - // m_visualizedSpacesMgr->SyncViews(predictedDisplayTime); - // - // //Notify the rest of the engine. - // AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, - // m_visualizedSpacesMgr->GetViewSpacePose(), - // m_visualizedSpacesMgr->GetViewPoses()); - // - // //GetNativeInput()->UpdateXrSpaceLocations(device, predictedDisplayTime, xrViews); + if (IsSessionFocused()) + { + // Syncing actions only works if the session is in focused state + m_actionsMgr->SyncActions(predictedDisplayTime); + } + + m_visualizedSpacesMgr->SyncViews(predictedDisplayTime); + + //Notify the rest of the engine. + AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, + m_visualizedSpacesMgr->GetViewSpacePose(), + m_visualizedSpacesMgr->GetViewPoses()); } } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index fb14ca7ef..e6bd40c24 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -19,6 +19,8 @@ #include #include +#include "OpenXRBehaviorReflection.h" + #include namespace OpenXRVk @@ -37,6 +39,11 @@ namespace OpenXRVk ->Version(1); } + if (auto behaviorContext = azrtti_cast(context)) + { + OpenXRBehaviorReflect(*behaviorContext); + } + AzFramework::InputDeviceXRController::Reflect(context); OpenXRInteractionProfilesAsset::Reflect(context); OpenXRActionSetsAsset::Reflect(context); diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index e705494d4..169180251 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -43,8 +43,10 @@ set(FILES Source/OpenXRActionSetDescriptor.cpp Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h -# Source/OpenXRActionsManager.cpp -# Source/OpenXRActionsManager.h -# Source/OpenXRVisualizedSpacesManager.cpp -# Source/OpenXRVisualizedSpacesManager.h + Source/OpenXRVisualizedSpacesManager.cpp + Source/OpenXRVisualizedSpacesManager.h + Source/OpenXRActionsManager.cpp + Source/OpenXRActionsManager.h + Source/OpenXRBehaviorReflection.cpp + Source/OpenXRBehaviorReflection.h ) From 0ca01ba286715f116bf83f3b44df92c8b4e090c4 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 26 Jan 2024 13:26:37 -0600 Subject: [PATCH 17/33] All the fundamentals are working. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Include/OpenXRVk/OpenXRActionSetsAsset.h | 97 ++++- .../OpenXRVk/OpenXRInteractionProfilesAsset.h | 8 +- .../Builders/OpenXRActionSetsAssetBuilder.cpp | 97 ++++- .../Builders/OpenXRActionSetsAssetBuilder.h | 10 +- .../OpenXRAssetBuildersSystemComponent.cpp | 6 +- .../Code/Source/OpenXRActionSetDescriptor.cpp | 86 +++- .../Code/Source/OpenXRActionSetsAsset.cpp | 384 +++++++++++++++++- .../Code/Source/OpenXRActionsManager.cpp | 127 ++++-- .../Code/Source/OpenXRActionsManager.h | 30 +- .../Code/Source/OpenXRVkSystemComponent.cpp | 4 +- .../Code/openxrvk_private_common_files.cmake | 2 - 11 files changed, 774 insertions(+), 77 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h index cc94bdd4d..828f116b5 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h @@ -9,26 +9,115 @@ #pragma once #include -#include -#include -#include "OpenXRActionSetDescriptor.h" +#include namespace OpenXRVk { + class OpenXRActionPathDescriptor final + { + public: + AZ_RTTI(OpenXRActionPathDescriptor, "{F25D6382-C9E0-414B-A542-1758F5477D03}"); + virtual ~OpenXRActionPathDescriptor() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + AZStd::string GetEditorText() const; + + AZStd::string m_interactionProfileName; + AZStd::string m_userPathName; + AZStd::string m_componentPathName; + + private: + AZ::Crc32 OnInteractionProfileSelected(); + AZStd::vector GetInteractionProfiles() const; + + AZ::Crc32 OnUserPathSelected(); + AZStd::vector GetUserPaths() const; + + AZ::Crc32 OnComponentPathSelected(); + AZStd::vector GetComponentPaths() const; + }; + + class OpenXRActionDescriptor final + { + public: + AZ_RTTI(OpenXRActionDescriptor, "{90BBF6F6-C7D6-4F64-B784-CE03F86DC36B}"); + virtual ~OpenXRActionDescriptor() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + AZStd::string GetEditorText() const; + + AZStd::string m_name; // Regular char* + AZStd::string m_localizedName; // UTF-8 string. + //! List of I/O action paths that will be bound to this action. + //! The first action path in this list, determines what type of action paths + //! can be added to the list. For example: + //! If the first action path happens to be a boolean, then subsequent action paths + //! can only be added if they can map to a boolean. + //! Another important case is if the this is a haptic feedback action (Output), then + //! subsequent action paths can only be of type haptic feedback actions. + AZStd::vector m_actionPathDescriptors; + }; + + class OpenXRActionSetDescriptor final + { + public: + AZ_RTTI(OpenXRActionSetDescriptor, "{3A08BC1F-656F-441F-89C3-829F95B9B329}"); + virtual ~OpenXRActionSetDescriptor() = default; + + static void Reflect(AZ::ReflectContext* reflection); + + AZStd::string GetEditorText() const; + + AZStd::string m_name; // Regular char* + AZStd::string m_localizedName; // UTF-8 string. + uint32_t m_priority = 0; // Higher values mean higher priority. + AZStd::vector m_actionDescriptors; + }; + //! This asset defines a list of OpenXR Action Sets that an application supports //! regarding inputs and haptics. class OpenXRActionSetsAsset final : public AZ::Data::AssetData { public: - AZ_RTTI(OpenXRActionSetsAsset, "{C2DEE370-6151-4701-AEA5-AEA3CA247CFF}", AZ::Data::AssetData); AZ_CLASS_ALLOCATOR(OpenXRActionSetsAsset, AZ::SystemAllocator); + AZ_RTTI(OpenXRActionSetsAsset, "{C2DEE370-6151-4701-AEA5-AEA3CA247CFF}", AZ::Data::AssetData); static void Reflect(AZ::ReflectContext* context); + OpenXRActionSetsAsset(); + static constexpr char s_assetTypeName[] = "OpenXR Action Sets Asset"; static constexpr char s_assetExtension[] = "xractions"; + AZ::Data::Asset m_interactionProfilesAsset; AZStd::vector m_actionSetDescriptors; + + private: + AZ::Crc32 OnInteractionProfilesAssetChanged(); + }; + + //! We need a custom asset handler because OpenXRActionSetsAsset contains a reference to another + //! asset of type OpenXRInteractionProfilesAsset, and we don't have a custom builder that declares the + //! dependency in the Asset Catalog database. Without this custom handler, the engine asserts + //! complaining about the missing dependency. + class OpenXRActionSetsAssetHandler final + : public AzFramework::GenericAssetHandler + { + public: + AZ_RTTI(OpenXRActionSetsAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); + AZ_CLASS_ALLOCATOR(OpenXRActionSetsAssetHandler, AZ::SystemAllocator); + + static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; + + OpenXRActionSetsAssetHandler(); + + // Called by the asset manager to perform actual asset load. + AZ::Data::AssetHandler::LoadResult LoadAssetData( + const AZ::Data::Asset& asset, + AZStd::shared_ptr stream, + const AZ::Data::AssetFilterCB& assetLoadFilterCB) override; }; }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h index 4bf5e2f93..05bfc27a1 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h @@ -8,13 +8,7 @@ #pragma once -#include -#include -#include -#include -#include #include - #include #include @@ -32,8 +26,8 @@ namespace OpenXRVk : public AZ::Data::AssetData { public: - AZ_RTTI(OpenXRInteractionProfilesAsset, "{02555DCD-E363-42FB-935C-4E67CC3A1699}", AZ::Data::AssetData); AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAsset, AZ::SystemAllocator); + AZ_RTTI(OpenXRInteractionProfilesAsset, "{02555DCD-E363-42FB-935C-4E67CC3A1699}", AZ::Data::AssetData); static void Reflect(AZ::ReflectContext* context); static constexpr char s_assetTypeName[] = "OpenXR Interaction Profiles"; diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp index 3ca1d02cf..de97c8763 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "OpenXRActionSetsAssetBuilder.h" @@ -36,7 +37,13 @@ namespace OpenXRVkBuilders bool result = AZ::StringFunc::Path::GetExtension(request.m_sourceFile.c_str(), fileExtension, includeDot); if (result && (fileExtension == OpenXRVk::OpenXRInteractionProfilesAsset::s_assetExtension)) { - CreateInteractionProfilesJobs(request, response); + CreateInteractionProfilesAssetJobs(request, response); + return; + } + + if (result && (fileExtension == OpenXRVk::OpenXRActionSetsAsset::s_assetExtension)) + { + CreateActionSetsAssetJobs(request, response); return; } @@ -54,11 +61,19 @@ namespace OpenXRVkBuilders bool result = AZ::StringFunc::Path::GetExtension(request.m_sourceFile.c_str(), fileExtension, includeDot); if (result && (fileExtension == OpenXRVk::OpenXRInteractionProfilesAsset::s_assetExtension)) { - ProcessInteractionProfilesJob(request, response); + ProcessInteractionProfilesAssetJob(request, response); + return; + } + if (result && (fileExtension == OpenXRVk::OpenXRActionSetsAsset::s_assetExtension)) + { + ProcessActionSetsAssetJob(request, response); } } - void OpenXRActionSetsAssetBuilder::CreateInteractionProfilesJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const + + ///////////////////////////////////////////////////////////////////////////////// + // OpenXRInteractionProfilesAsset Support Begin + void OpenXRActionSetsAssetBuilder::CreateInteractionProfilesAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms) { @@ -67,7 +82,7 @@ namespace OpenXRVkBuilders // and initialize the I/O actions system. jobDescriptor.m_priority = 1000; jobDescriptor.m_critical = true; - jobDescriptor.m_jobKey = JobKey; + jobDescriptor.m_jobKey = InteractionProfilesAssetJobKey; jobDescriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str()); response.m_createJobOutputs.emplace_back(AZStd::move(jobDescriptor)); } // for all request.m_enabledPlatforms @@ -75,7 +90,7 @@ namespace OpenXRVkBuilders response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } - void OpenXRActionSetsAssetBuilder::ProcessInteractionProfilesJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const + void OpenXRActionSetsAssetBuilder::ProcessInteractionProfilesAssetJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const { // Open the file, and make sure there's no redundant data, the OpenXR Paths are well formatted, etc. auto interactionProfilesAssetPtr = AZ::Utils::LoadObjectFromFile(request.m_fullPath); @@ -114,6 +129,78 @@ namespace OpenXRVkBuilders response.m_outputProducts.emplace_back(AZStd::move(jobProduct)); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; } + // OpenXRInteractionProfilesAsset Support End + ///////////////////////////////////////////////////////////////////////////////////// + + void OpenXRActionSetsAssetBuilder::CreateActionSetsAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const + { + for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms) + { + if (platformInfo.m_identifier != "pc") + { + continue; + } + AssetBuilderSDK::JobDescriptor jobDescriptor; + // Very high priority because this asset is required to initialize the OpenXR runtime + // and initialize the I/O actions system. + jobDescriptor.m_priority = 999; + jobDescriptor.m_critical = true; + jobDescriptor.m_jobKey = ActionSetsAssetJobKey; + jobDescriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str()); + response.m_createJobOutputs.emplace_back(AZStd::move(jobDescriptor)); + } // for all request.m_enabledPlatforms + + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; + } + + void OpenXRActionSetsAssetBuilder::ProcessActionSetsAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const + { + AZ::ObjectStream::FilterDescriptor loadFilter = AZ::ObjectStream::FilterDescriptor(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES); + auto actionSetsAssetPtr = AZ::Utils::LoadObjectFromFile(request.m_fullPath, nullptr, loadFilter); + if (!actionSetsAssetPtr) + { + AZ_Error(LogName, false, "Failed to LoadObjectFromFile %s", request.m_fullPath.c_str()); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } + + // The Action Sets Asset contains an asset reference to the OpenXRInteractionProfilesAsset that was used + // to construct the data in it. Because we are running in a builder context, the OpenXRInteractionProfilesAsset + // is loaded with a null handle, BUT the AssetHint is valid and we'll use the AssetHint to discover + // the OpenXRInteractionProfilesAsset and load it manually. + + + // We keep exact same asset name and extension. + AZStd::string assetFileName; + AZ::StringFunc::Path::GetFullFileName(request.m_fullPath.c_str(), assetFileName); + + // Construct product full path + AZStd::string assetOutputPath; + AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), assetFileName.c_str(), assetOutputPath, true); + + bool result = AZ::Utils::SaveObjectToFile(assetOutputPath, AZ::DataStream::ST_XML, actionSetsAssetPtr); + if (result == false) + { + AZ_Error(LogName, false, "Failed to save asset to %s", assetOutputPath.c_str()); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } + + // This step is very important, because it declares product dependency between ShaderAsset and the root ShaderVariantAssets (one for each supervariant). + // This will guarantee that when the ShaderAsset is loaded at runtime, the ShaderAsset will report OnAssetReady only after the root ShaderVariantAssets + // are already fully loaded and ready. + AssetBuilderSDK::JobProduct jobProduct; + if (!AssetBuilderSDK::OutputObject(actionSetsAssetPtr, assetOutputPath, azrtti_typeid(), + aznumeric_cast(0), jobProduct)) + { + AZ_Error(LogName, false, "FIXME this message."); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } + response.m_outputProducts.emplace_back(AZStd::move(jobProduct)); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success; + } + } // namespace OpenXRVkBuilders diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h index 2eff33cf4..0e5c927bc 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h @@ -19,7 +19,8 @@ namespace OpenXRVkBuilders public: AZ_TYPE_INFO(OpenXRActionSetsAssetBuilder, "{1D053000-7799-459D-B99B-FF6AE6394BC1}"); - static constexpr const char* JobKey = "XR Action Sets Asset"; + static constexpr const char* InteractionProfilesAssetJobKey = "XR Interaction Profiles Asset"; + static constexpr const char* ActionSetsAssetJobKey = "XR Action Sets Asset"; OpenXRActionSetsAssetBuilder() = default; ~OpenXRActionSetsAssetBuilder() = default; @@ -36,8 +37,11 @@ namespace OpenXRVkBuilders static constexpr char LogName[] = "OpenXRActionSetsAssetBuilder"; - void CreateInteractionProfilesJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; - void ProcessInteractionProfilesJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const; + void CreateInteractionProfilesAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; + void ProcessInteractionProfilesAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const; + + void CreateActionSetsAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; + void ProcessActionSetsAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const; }; } // namespace OpenXRVkBuilders diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp index 1c2d768e7..a27cd3203 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp @@ -10,6 +10,7 @@ #include #include +#include #include "OpenXRAssetBuildersSystemComponent.h" @@ -18,6 +19,7 @@ namespace OpenXRVkBuilders void OpenXRAssetBuildersSystemComponent::Reflect(AZ::ReflectContext* context) { OpenXRVk::OpenXRInteractionProfilesAsset::Reflect(context); + OpenXRVk::OpenXRActionSetsAsset::Reflect(context); if (AZ::SerializeContext* serialize = azrtti_cast(context)) { @@ -58,8 +60,8 @@ namespace OpenXRVkBuilders AssetBuilderSDK::AssetBuilderDesc assetBuilderDescriptor; assetBuilderDescriptor.m_name = "OpenXR ActionSets Builder"; assetBuilderDescriptor.m_version = 1; // First versuib - assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRActionSetsAssetBuilder::InteractionProfilesSourceFileExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - //assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRActionSetsAssetBuilder::ActionSetsSourceFileExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); + assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRVk::OpenXRInteractionProfilesAsset::s_assetExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); + assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRVk::OpenXRActionSetsAsset::s_assetExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); assetBuilderDescriptor.m_busId = azrtti_typeid(); assetBuilderDescriptor.m_createJobFunction = AZStd::bind(&OpenXRActionSetsAssetBuilder::CreateJobs, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); assetBuilderDescriptor.m_processJobFunction = AZStd::bind(&OpenXRActionSetsAssetBuilder::ProcessJob, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp index 4aa3c9c14..d96fc005b 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp @@ -6,11 +6,36 @@ * */ -#include "InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h" +#include + +#include #include namespace OpenXRVk { + // This function is only used when an OpenXRActionSetsAsset is being created/edited with the AssetEditor. + static const AZ::Data::Asset& GetInteractionProfilesAsset() + { + static AZ::Data::Asset s_asset; + if (!s_asset.IsReady()) + { + const auto interactionProfilesAssetPath = OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath(); + if (interactionProfilesAssetPath.empty()) + { + AZ_Error("OpenXRActionPathDescriptor", false, "No interaction profile asset has been defined"); + return s_asset; + } + s_asset = AZ::RPI::AssetUtils::LoadCriticalAsset(interactionProfilesAssetPath); + if (!s_asset.IsReady()) + { + AZ_Error("OpenXRActionPathDescriptor", false, "The system interaction profiles asset [%s] is not ready. There's no data available to create/edit an OpenXRActionSetsAsset", + interactionProfilesAssetPath.c_str()); + return s_asset; + } + } + return s_asset; + } + /////////////////////////////////////////////////////////// /// OpenXRActionPathDescriptor void OpenXRActionPathDescriptor::Reflect(AZ::ReflectContext* context) @@ -58,12 +83,24 @@ namespace OpenXRVk AZStd::vector OpenXRActionPathDescriptor::GetInteractionProfiles() const { - auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); - if (!interactionProviderIface) + // auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); + // if (!interactionProviderIface) + // { + // return {}; + // } + // return interactionProviderIface->GetInteractionProfileNames(); + + const auto& interactionProfilesAsset = GetInteractionProfilesAsset(); + if (!interactionProfilesAsset.IsReady()) { return {}; } - return interactionProviderIface->GetInteractionProfileNames(); + AZStd::vector profileNames; + for (const auto& profileDescriptor : interactionProfilesAsset->m_interactionProfileDescriptors) + { + profileNames.push_back(profileDescriptor.m_uniqueName); + } + return profileNames; } AZ::Crc32 OpenXRActionPathDescriptor::OnUserPathSelected() @@ -73,14 +110,14 @@ namespace OpenXRVk AZStd::vector OpenXRActionPathDescriptor::GetUserPaths() const { - AZStd::vector retList; - auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); - if (!interactionProviderIface) + const auto& interactionProfilesAsset = GetInteractionProfilesAsset(); + if (!interactionProfilesAsset.IsReady()) { - return retList; + return {}; } - const auto * profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfileName); + AZStd::vector retList; + const auto profileDescriptor = interactionProfilesAsset->GetInteractionProfileDescriptor(m_interactionProfileName); if (!profileDescriptor) { return retList; @@ -90,6 +127,25 @@ namespace OpenXRVk retList.push_back(userPathDescriptor.m_name); } return retList; + + + // AZStd::vector retList; + // auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); + // if (!interactionProviderIface) + // { + // return retList; + // } + // + // const auto * profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfileName); + // if (!profileDescriptor) + // { + // return retList; + // } + // for (const auto& userPathDescriptor : profileDescriptor->m_userPathDescriptors) + // { + // retList.push_back(userPathDescriptor.m_name); + // } + // return retList; } AZ::Crc32 OpenXRActionPathDescriptor::OnComponentPathSelected() @@ -99,18 +155,20 @@ namespace OpenXRVk AZStd::vector OpenXRActionPathDescriptor::GetComponentPaths() const { - AZStd::vector retList; - auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); - if (!interactionProviderIface) + const auto& interactionProfilesAsset = GetInteractionProfilesAsset(); + if (!interactionProfilesAsset.IsReady()) { - return retList; + return {}; } - const auto* profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfileName); + AZStd::vector retList; + + const auto profileDescriptor = interactionProfilesAsset->GetInteractionProfileDescriptor(m_interactionProfileName); if (!profileDescriptor) { return retList; } + const auto* userPathDescriptor = profileDescriptor->GetUserPathDescriptor(m_userPathName); if (!userPathDescriptor) { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp index c09f4e495..0d18e7da7 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp @@ -6,14 +6,336 @@ * */ -#include -#include -#include +//! Breadcrumb... Because this asset serializes a field of type AZ::Data::Asset<...> +//! We need to include this file first to avoid the following compiler error: +//! error C2027: use of undefined type 'AZ::SerializeGenericTypeInfo' +//! error C3861: 'GetClassTypeId': identifier not found +//! The error is triggered when calling ->Field("InteractionProfilesAsset", &OpenXRActionSetsAsset::m_interactionProfilesAsset) +#include #include namespace OpenXRVk { + // // This function is only used when an OpenXRActionSetsAsset is being created/edited with the AssetEditor. + // static const AZ::Data::Asset& GetInteractionProfilesAsset() + // { + // static AZ::Data::Asset s_asset; + // if (!s_asset.IsReady()) + // { + // const auto interactionProfilesAssetPath = OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath(); + // if (interactionProfilesAssetPath.empty()) + // { + // AZ_Error("OpenXRActionPathDescriptor", false, "No interaction profile asset has been defined"); + // return s_asset; + // } + // s_asset = AZ::RPI::AssetUtils::LoadCriticalAsset(interactionProfilesAssetPath); + // if (!s_asset.IsReady()) + // { + // AZ_Error("OpenXRActionPathDescriptor", false, "The system interaction profiles asset [%s] is not ready. There's no data available to create/edit an OpenXRActionSetsAsset", + // interactionProfilesAssetPath.c_str()); + // return s_asset; + // } + // } + // return s_asset; + // } + + namespace EditorInternal + { + // This static asset reference is only relevant when the user is creating an OpenXRActionSetsAsset with the + // Asset Editor. Because this variable is a singleton, creating two of these assets at the same time won't be possible. + // But this is not an issue because most likely all OpenXRActionSetsAsset always use the same OpenXRInteractionProfilesAsset. + static AZ::Data::Asset s_asset; + static constexpr char LogName[] = "EditorInternal::OpenXRInteractionProfilesAsset"; + + static void BlockingReloadAssetIfNotReady() + { + if (!s_asset.GetId().IsValid()) + { + return; + } + if (!s_asset.IsReady()) + { + s_asset.QueueLoad(); + if (s_asset.IsLoading()) + { + s_asset.BlockUntilLoadComplete(); + } + } + } + + static void SetCurrentInteractionProfilesAsset(AZ::Data::Asset& newProfilesAsset, + bool loadAsset = true) + { + if (!newProfilesAsset.GetId().IsValid()) + { + AZ_Printf(LogName, "The user cleared the global OpenXRInteractionProfilesAsset used for ActionSets Asset Editing."); + s_asset = {}; + return; + } + s_asset = newProfilesAsset; + if (loadAsset) + { + BlockingReloadAssetIfNotReady(); + } + } + + static const AZ::Data::Asset& GetCurrentInteractionProfilesAsset(bool loadAsset = true) + { + if (loadAsset) + { + BlockingReloadAssetIfNotReady(); + } + return s_asset; + } + } + + /////////////////////////////////////////////////////////// + /// OpenXRActionPathDescriptor + void OpenXRActionPathDescriptor::Reflect(AZ::ReflectContext* context) + { + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("InteractionProfile", &OpenXRActionPathDescriptor::m_interactionProfileName) + ->Field("UserPath", &OpenXRActionPathDescriptor::m_userPathName) + ->Field("ComponentPath", &OpenXRActionPathDescriptor::m_componentPathName) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRActionPathDescriptor", "A specific OpenXR I/O action path.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionPathDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_interactionProfileName, "Interaction Profile", "The name of the Interaction Profile.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnInteractionProfileSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetInteractionProfiles) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_userPathName, "User Path", "Name of the User Path.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnUserPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetUserPaths) + ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRActionPathDescriptor::m_componentPathName, "I/O Component Path", "The name of I/O Component Path.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionPathDescriptor::OnComponentPathSelected) + ->Attribute(AZ::Edit::Attributes::StringList, &OpenXRActionPathDescriptor::GetComponentPaths) + ; + } + } + } + + AZStd::string OpenXRActionPathDescriptor::GetEditorText() const + { + return AZStd::string::format("%s %s %s", m_interactionProfileName.c_str(), m_userPathName.c_str(), m_componentPathName.c_str()); + } + + AZ::Crc32 OpenXRActionPathDescriptor::OnInteractionProfileSelected() + { + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } + + AZStd::vector OpenXRActionPathDescriptor::GetInteractionProfiles() const + { + // auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); + // if (!interactionProviderIface) + // { + // return {}; + // } + // return interactionProviderIface->GetInteractionProfileNames(); + + const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset(); + if (!interactionProfilesAsset.IsReady()) + { + return {}; + } + AZStd::vector profileNames; + for (const auto& profileDescriptor : interactionProfilesAsset->m_interactionProfileDescriptors) + { + profileNames.push_back(profileDescriptor.m_uniqueName); + } + return profileNames; + } + + AZ::Crc32 OpenXRActionPathDescriptor::OnUserPathSelected() + { + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } + + AZStd::vector OpenXRActionPathDescriptor::GetUserPaths() const + { + const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset(); + if (!interactionProfilesAsset.IsReady()) + { + return {}; + } + + AZStd::vector retList; + const auto profileDescriptor = interactionProfilesAsset->GetInteractionProfileDescriptor(m_interactionProfileName); + if (!profileDescriptor) + { + return retList; + } + for (const auto& userPathDescriptor : profileDescriptor->m_userPathDescriptors) + { + retList.push_back(userPathDescriptor.m_name); + } + return retList; + + + // AZStd::vector retList; + // auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); + // if (!interactionProviderIface) + // { + // return retList; + // } + // + // const auto * profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfileName); + // if (!profileDescriptor) + // { + // return retList; + // } + // for (const auto& userPathDescriptor : profileDescriptor->m_userPathDescriptors) + // { + // retList.push_back(userPathDescriptor.m_name); + // } + // return retList; + } + + AZ::Crc32 OpenXRActionPathDescriptor::OnComponentPathSelected() + { + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } + + AZStd::vector OpenXRActionPathDescriptor::GetComponentPaths() const + { + const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset(); + if (!interactionProfilesAsset.IsReady()) + { + return {}; + } + + AZStd::vector retList; + + const auto profileDescriptor = interactionProfilesAsset->GetInteractionProfileDescriptor(m_interactionProfileName); + if (!profileDescriptor) + { + return retList; + } + + const auto* userPathDescriptor = profileDescriptor->GetUserPathDescriptor(m_userPathName); + if (!userPathDescriptor) + { + return retList; + } + + for (const auto& componentPath : userPathDescriptor->m_componentPathDescriptors) + { + retList.push_back(componentPath.m_name); + } + + for (const auto& componentPath : profileDescriptor->m_commonComponentPathDescriptors) + { + retList.push_back(componentPath.m_name); + } + + return retList; + } + /// OpenXRActionPathDescriptor + /////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////// + /// OpenXRActionDescriptor + void OpenXRActionDescriptor::Reflect(AZ::ReflectContext* context) + { + OpenXRActionPathDescriptor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRActionDescriptor::m_name) + ->Field("LocalizedName", &OpenXRActionDescriptor::m_localizedName) + ->Field("ActionPathDescriptors", &OpenXRActionDescriptor::m_actionPathDescriptors) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRActionDescriptor", "An action bound to one or more OpenXR Action Paths.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_name, "Name", "Runtime identifier for this action.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_localizedName, "Localized Name", "User friendly display name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_actionPathDescriptors, "Action Paths", "List of action paths bound to this action") + ; + } + } + } + + AZStd::string OpenXRActionDescriptor::GetEditorText() const + { + if (!m_localizedName.empty()) + { + return m_localizedName; + } + return m_name.empty() ? "" : m_name; + } + /// OpenXRActionDescriptor + /////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////// + /// OpenXRActionSetDescriptor + void OpenXRActionSetDescriptor::Reflect(AZ::ReflectContext* context) + { + OpenXRActionDescriptor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Field("Name", &OpenXRActionSetDescriptor::m_name) + ->Field("LocalizedName", &OpenXRActionSetDescriptor::m_localizedName) + ->Field("Priority", &OpenXRActionSetDescriptor::m_priority) + ->Field("ActionDescriptors", &OpenXRActionSetDescriptor::m_actionDescriptors) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class("OpenXRActionSetDescriptor", "A group of OpenXR Actions that can be selectively activated/deactivated at runtime.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRActionSetDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_name, "Name", "Runtime identifier for this action set.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_localizedName, "Localized Name", "Action set display name.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSetDescriptor::m_priority, "Priority", "The higher this value the higher the priority.") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_actionDescriptors, "Actions", "List of actions for this action set.") + ; + } + } + } + + AZStd::string OpenXRActionSetDescriptor::GetEditorText() const + { + if (!m_localizedName.empty()) + { + return m_localizedName; + } + return m_name.empty() ? "" : m_name; + } + /// OpenXRActionSetDescriptor + /////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////// /// OpenXRActionBindingsAsset void OpenXRActionSetsAsset::Reflect(AZ::ReflectContext* context) @@ -26,6 +348,7 @@ namespace OpenXRVk serialize->Class() ->Version(1) ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) + ->Field("InteractionProfilesAsset", &OpenXRActionSetsAsset::m_interactionProfilesAsset) ->Field("ActionSetDescriptors", &OpenXRActionSetsAsset::m_actionSetDescriptors) ; @@ -36,12 +359,67 @@ namespace OpenXRVk s_assetTypeName, "Defines the OpenXR Actions an application cares about.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetsAsset::m_interactionProfilesAsset, "Interaction Profiles Asset", "This asset determines the hardware systems that are compatible with these Action Sets.") + ->Attribute(AZ::Edit::Attributes::ChangeNotify, &OpenXRActionSetsAsset::OnInteractionProfilesAssetChanged) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetsAsset::m_actionSetDescriptors, "Action Sets", "List of action sets.") ; } } } + + OpenXRActionSetsAsset::OpenXRActionSetsAsset() : AZ::Data::AssetData() + { + // The reason we don't load the interaction profiles asset upon construction + // is because we only want to load the singleton asset only if we are 100% sure + // this asset is being edited by the Asset Editor. + // Remember that at game runtime, there's no such thing as the Asset Editor. + //constexpr bool loadAsset = false; + //EditorInternal::SetCurrentInteractionProfilesAsset(m_interactionProfilesAsset, loadAsset); + } + + AZ::Crc32 OpenXRActionSetsAsset::OnInteractionProfilesAssetChanged() + { + EditorInternal::SetCurrentInteractionProfilesAsset(m_interactionProfilesAsset); + return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; + } /// OpenXRActionBindingsAsset /////////////////////////////////////////////////////////// + + OpenXRActionSetsAssetHandler::OpenXRActionSetsAssetHandler() + : AzFramework::GenericAssetHandler( + OpenXRActionSetsAsset::s_assetTypeName, + "Other", + OpenXRActionSetsAsset::s_assetExtension) + { + } + + AZ::Data::AssetHandler::LoadResult OpenXRActionSetsAssetHandler::LoadAssetData( + const AZ::Data::Asset& asset, + AZStd::shared_ptr stream, + const AZ::Data::AssetFilterCB& assetLoadFilterCB) + { + auto actionSetsAsset = asset.GetAs(); + if (!actionSetsAsset) + { + AZ_Error("OpenXRActionSetsAssetHandler", false, "This should be a OpenXRActionSetsAsset, as this is the only type we process."); + return AZ::Data::AssetHandler::LoadResult::Error; + } + + if (!AZ::Utils::LoadObjectFromStreamInPlace( + *stream, *actionSetsAsset, nullptr, AZ::ObjectStream::FilterDescriptor(assetLoadFilterCB))) + { + return AZ::Data::AssetHandler::LoadResult::Error; + } + + // The reason we don't load the interaction profiles asset upon construction + // is because we only want to load the singleton asset only if we are 100% sure + // this asset is being edited by the Asset Editor. + // Remember that at game runtime, there's no such thing as the Asset Editor. + constexpr bool loadAsset = false; + EditorInternal::SetCurrentInteractionProfilesAsset(actionSetsAsset->m_interactionProfilesAsset, loadAsset); + + return AZ::Data::AssetHandler::LoadResult::LoadComplete; + + } } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp index aacb0cce0..e15168997 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp @@ -6,6 +6,8 @@ * */ +#include + #include #include @@ -58,6 +60,55 @@ namespace OpenXRVk return componentPathDescriptor->GetXrActionType(); } + AZStd::string ActionsManager::GetActionSetsAssetPath() + { + // FIXME! Should be registry key also. + return AZStd::string(DefaultActionsAssetPath); + } + + bool ActionsManager::StartActionSetAssetAsync() + { + // // There are two critical assets that need to be loaded. + // // The first asset defines the list of interaction profiles supported by the current version + // // of the OpenXR Gem. + // const auto interactionProfilesAssetPath = OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath(); + // if (interactionProfilesAssetPath.empty()) + // { + // AZ_Warning(LogName, false, "No interaction profile asset has been defined. This application will run without user interaction support."); + // return true; + // } + // const auto interactionProfilesAsset = AZ::RPI::AssetUtils::LoadCriticalAsset(interactionProfilesAssetPath); + // if (!interactionProfilesAsset.IsReady()) + // { + // AZ_Warning(LogName, false, "The system interaction profiles asset [%s] is not ready. This application will run without user interaction support.", + // interactionProfilesAssetPath.c_str()); + // return true; + // } + + auto productPath = GetActionSetsAssetPath(); + auto assetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(productPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error); + if (!assetId.IsValid()) + { + AZ_Error(LogName, false, "No ActionSet Asset named [%s] was found in the asset catalog.", productPath.c_str()); + 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. + } + + AZ::Data::AssetBus::Handler::BusConnect(assetId); + + m_actionSetAsset = AZ::Data::AssetManager::Instance().GetAsset(assetId, + azrtti_typeid(), AZ::Data::AssetLoadBehavior::PreLoad); + m_actionSetAsset.QueueLoad(); + return true; + } + + ActionsManager::~ActionsManager() + { + if (AZ::Data::AssetBus::Handler::BusIsConnected()) + { + AZ::Data::AssetBus::Handler::BusDisconnect(); + } + } + bool ActionsManager::Init(XrInstance xrInstance, XrSession xrSession) { @@ -75,47 +126,29 @@ namespace OpenXRVk return false; } - // There are two critical assets that need to be loaded. - // The first asset defines the list of interaction profiles supported by the current version - // of the OpenXR Gem. - const auto interactionProfilesAssetPath = OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath(); - if (interactionProfilesAssetPath.empty()) - { - AZ_Warning(LogName, false, "No interaction profile asset has been defined. This application will run without user interaction support."); - return true; - } - const auto interactionProfilesAsset = AZ::RPI::AssetUtils::LoadCriticalAsset(interactionProfilesAssetPath); - if (!interactionProfilesAsset.IsReady()) - { - AZ_Warning(LogName, false, "The system interaction profiles asset [%s] is not ready. This application will run without user interaction support.", - interactionProfilesAssetPath.c_str()); - return true; - } + return StartActionSetAssetAsync(); + } - // OpenXR only allows to define ActionSets during session creation. - // From the point of view of O3DE, the developer defines action sets - // in an asset of type OpenXRActionBindingsAsset. - // The default source path for said asset is "@project@/openxr.xractions". - m_actionSetAsset = AZ::RPI::AssetUtils::LoadCriticalAsset({ DefaultActionsAssetPath }); - if (!m_actionSetAsset.IsReady()) - { - AZ_Warning(LogName, false, "Action Sets asset [%s] not found. This application will run without user interaction support.", DefaultActionsAssetPath); - return true; - } + void ActionsManager::InitInternal() + { + AZ_Assert(m_actionSetAsset.IsReady(), "Action Sets asset should be ready!"); + AZ_Assert(m_actionSetAsset->m_interactionProfilesAsset.IsReady(), "FIXME GALIB Be Readu!!"); + + const auto interactionProfilesAsset = m_actionSetAsset->m_interactionProfilesAsset; SuggestedBindingsPerProfile suggestedBindingsPerProfile; for (const auto& actionSetDescriptor : m_actionSetAsset->m_actionSetDescriptors) { if (!InitActionSet(*interactionProfilesAsset, actionSetDescriptor, suggestedBindingsPerProfile)) { - return false; + return; } } if (suggestedBindingsPerProfile.empty()) { AZ_Printf(LogName, "This application will run without actions.\n"); - return true; + return; } // Register the bindings for each active interaction profile. @@ -140,7 +173,7 @@ namespace OpenXRVk if (activeProfileCount < 1) { AZ_Error(LogName, false, "Failed to activate at least one interaction profile. This application will run without actions.\n"); - return true; + return; } AZStd::vector xrActionSets; @@ -162,10 +195,9 @@ namespace OpenXRVk { m_xrActiveActionSets.clear(); PrintXrError(LogName, result, "Failed to attach %zu action sets to the session.", xrActionSets.size()); - return false; } - return true; + AZ_Printf(LogName, "Successfully configure the Action Sets."); } bool ActionsManager::SyncActions(XrTime predictedDisplayTime) @@ -770,6 +802,8 @@ namespace OpenXRVk } /// OpenXRActionsInterface overrides ///////////////////////////////////////////////// + + AZ::Outcome ActionsManager::ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets) { // First get the index. @@ -799,6 +833,7 @@ namespace OpenXRVk return AZ::Success(true); } + void ActionsManager::RecreateXrActiveActionSets() { // Recreate and cache the XrActionActionSet list. @@ -813,4 +848,34 @@ namespace OpenXRVk } } + + ///////////////////////////////////////////////// + /// AssetBus overrides + void ActionsManager::OnAssetReady(AZ::Data::Asset asset) + { + AZ::Data::AssetBus::Handler::BusDisconnect(); + + AZ_Printf(LogName, "Successfully loaded the Action Sets Asset [%s].\n", asset.GetHint().c_str()); + + // Finish ActionsManager initialization on the main loop. + AZ::TickBus::QueueFunction([this, asset]() + { + m_actionSetAsset = asset; + InitInternal(); + }); + } + + void ActionsManager::OnAssetError(AZ::Data::Asset asset) + { + AZ::Data::AssetBus::Handler::BusDisconnect(); + + m_actionSetAsset = asset; + + // Report the error and do nothing. + AZ_Assert(false, "Application failed to load The Action Sets asset. This application will keep running without user interaction support."); + AZ_Error(LogName, false, "Application failed to load The Action Sets asset. This application will keep running without user interaction support."); + } + /// AssetBus overrides + ///////////////////////////////////////////////// + } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h index 9f5c8ce72..e94b3bf6d 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h @@ -16,7 +16,6 @@ #include #include -#include #include namespace OpenXRVk @@ -26,7 +25,8 @@ namespace OpenXRVk // asset created by the developer where all actions of interest // are instantiated. class ActionsManager final : - public OpenXRActionsInterface::Registrar + public OpenXRActionsInterface::Registrar, + private AZ::Data::AssetBus::Handler { public: AZ_CLASS_ALLOCATOR(ActionsManager, AZ::SystemAllocator); @@ -34,6 +34,9 @@ namespace OpenXRVk static constexpr char LogName[] = "OpenXRVkActionsManager"; + ActionsManager() = default; + ~ActionsManager(); + //! Initialize various actions and actionSets according to the //! "openxr.xractions" action bindings asset. bool Init(XrInstance xrInstance, XrSession xrSession); @@ -111,6 +114,9 @@ namespace OpenXRVk AZStd::unordered_map m_actions; }; + AZStd::string GetActionSetsAssetPath(); + bool StartActionSetAssetAsync(); + bool InitActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, const OpenXRActionSetDescriptor& actionSetDescriptor, SuggestedBindingsPerProfile& suggestedBindingsPerProfileOut); @@ -129,15 +135,29 @@ namespace OpenXRVk SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const; - AZ::Outcome ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false); void RecreateXrActiveActionSets(); + + ///////////////////////////////////////////////// + /// AssetBus overrides + //! The reason we don't care for OnAssetReloaded() is because in OpenXR + //! once a group of ActionSets have been attached to the session, they become + //! immutable. To have different action sets, the session would have to be recreated. + void OnAssetReady(AZ::Data::Asset asset) override; + void OnAssetError(AZ::Data::Asset asset) override; + /// AssetBus overrides + ///////////////////////////////////////////////// + + // Called when m_actionSetAsset is ready. + void InitInternal(); + XrInstance m_xrInstance = XR_NULL_HANDLE; XrSession m_xrSession = XR_NULL_HANDLE; - // Load synchronously as critical assets upon initilization. - AZ::Data::Asset m_interactionProfilesAsset; + // Loaded Asynchronously. Once this asset is ready, the real initialization of the + // OpenXR Actions and ActionSets will occur. + // This asset is loaded once and never changes. AZ::Data::Asset m_actionSetAsset; // Updated each time SyncActions is called. diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index e6bd40c24..72488006c 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -91,7 +91,9 @@ namespace OpenXRVk void SystemComponent::Activate() { - m_actionSetsAssetHandler = AZStd::make_unique>(OpenXRActionSetsAsset::s_assetTypeName, "Other", OpenXRActionSetsAsset::s_assetExtension); + //m_actionSetsAssetHandler = AZStd::make_unique>(OpenXRActionSetsAsset::s_assetTypeName, "Other", OpenXRActionSetsAsset::s_assetExtension); + //m_actionSetsAssetHandler->Register(); + m_actionSetsAssetHandler = AZStd::make_unique(); m_actionSetsAssetHandler->Register(); m_interactionProfilesAssetHandler = AZStd::make_unique(); diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index 169180251..a7a47450b 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -21,7 +21,6 @@ set(FILES Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h Include/OpenXRVk/OpenXRInteractionProfilesAsset.h - Include/OpenXRVk/OpenXRActionSetDescriptor.h Include/OpenXRVk/OpenXRActionSetsAsset.h Source/InputDeviceXRController.cpp Source/OpenXRVkCommon.h @@ -40,7 +39,6 @@ set(FILES Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h Source/OpenXRActionSetsAsset.cpp - Source/OpenXRActionSetDescriptor.cpp Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h Source/OpenXRVisualizedSpacesManager.cpp From dcfda4e5a85fdf9a15490aad16b8d64a024b1adb Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:27:38 -0600 Subject: [PATCH 18/33] file renaming to OpenXRVk... Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- Gems/OpenXRVk/Code/CMakeLists.txt | 6 +- .../OpenXRVk/OpenXRActionSetDescriptor.h | 83 ----- .../OpenXRVk/OpenXRInteractionProfilesAsset.h | 62 ---- ...nSetsAsset.h => OpenXRVkActionSetsAsset.h} | 2 +- ...Interface.h => OpenXRVkActionsInterface.h} | 0 ...r.h => OpenXRVkInteractionProfilesAsset.h} | 46 ++- .../OpenXRVk/OpenXRVkSystemComponent.h | 4 +- ....h => OpenXRVkVisualizedSpacesInterface.h} | 0 ...cpp => OpenXRVkActionSetsAssetBuilder.cpp} | 0 ...der.h => OpenXRVkActionSetsAssetBuilder.h} | 0 ... OpenXRVkAssetBuildersSystemComponent.cpp} | 0 ...=> OpenXRVkAssetBuildersSystemComponent.h} | 0 ...erModule.cpp => OpenXRVkBuilderModule.cpp} | 0 .../OpenXRInteractionProfilesAsset.cpp | 102 ------- ...enXRInteractionProfilesProviderInterface.h | 68 ----- ...ractionProfilesProviderSystemComponent.cpp | 284 ------------------ ...teractionProfilesProviderSystemComponent.h | 101 ------- ...or.cpp => OpenXRVkActionSetDescriptor.cpp} | 0 ...sAsset.cpp => OpenXRVkActionSetsAsset.cpp} | 25 +- ...Manager.cpp => OpenXRVkActionsManager.cpp} | 4 +- ...ionsManager.h => OpenXRVkActionsManager.h} | 4 +- ...ion.cpp => OpenXRVkBehaviorReflection.cpp} | 6 +- ...lection.h => OpenXRVkBehaviorReflection.h} | 0 Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp | 2 +- ...p => OpenXRVkInteractionProfilesAsset.cpp} | 110 ++++++- Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp | 3 - Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 4 +- .../Code/Source/OpenXRVkSystemComponent.cpp | 2 +- ...pp => OpenXRVkVisualizedSpacesManager.cpp} | 2 +- ...er.h => OpenXRVkVisualizedSpacesManager.h} | 2 +- .../Code/Source/XRCameraMovementComponent.cpp | 2 +- .../Code/openxrvk_private_builder_files.cmake | 8 +- .../Code/openxrvk_private_common_files.cmake | 29 +- .../Code/openxrvk_shared_builder_files.cmake | 2 +- 34 files changed, 174 insertions(+), 789 deletions(-) delete mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetDescriptor.h delete mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h rename Gems/OpenXRVk/Code/Include/OpenXRVk/{OpenXRActionSetsAsset.h => OpenXRVkActionSetsAsset.h} (98%) rename Gems/OpenXRVk/Code/Include/OpenXRVk/{OpenXRActionsInterface.h => OpenXRVkActionsInterface.h} (100%) rename Gems/OpenXRVk/Code/Include/OpenXRVk/{OpenXRInteractionProfileDescriptor.h => OpenXRVkInteractionProfilesAsset.h} (67%) rename Gems/OpenXRVk/Code/Include/OpenXRVk/{OpenXRVisualizedSpacesInterface.h => OpenXRVkVisualizedSpacesInterface.h} (100%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRActionSetsAssetBuilder.cpp => OpenXRVkActionSetsAssetBuilder.cpp} (100%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRActionSetsAssetBuilder.h => OpenXRVkActionSetsAssetBuilder.h} (100%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRAssetBuildersSystemComponent.cpp => OpenXRVkAssetBuildersSystemComponent.cpp} (100%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRAssetBuildersSystemComponent.h => OpenXRVkAssetBuildersSystemComponent.h} (100%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRBuilderModule.cpp => OpenXRVkBuilderModule.cpp} (100%) delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp delete mode 100644 Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h rename Gems/OpenXRVk/Code/Source/{OpenXRActionSetDescriptor.cpp => OpenXRVkActionSetDescriptor.cpp} (100%) rename Gems/OpenXRVk/Code/Source/{OpenXRActionSetsAsset.cpp => OpenXRVkActionSetsAsset.cpp} (93%) rename Gems/OpenXRVk/Code/Source/{OpenXRActionsManager.cpp => OpenXRVkActionsManager.cpp} (99%) rename Gems/OpenXRVk/Code/Source/{OpenXRActionsManager.h => OpenXRVkActionsManager.h} (98%) rename Gems/OpenXRVk/Code/Source/{OpenXRBehaviorReflection.cpp => OpenXRVkBehaviorReflection.cpp} (98%) rename Gems/OpenXRVk/Code/Source/{OpenXRBehaviorReflection.h => OpenXRVkBehaviorReflection.h} (100%) rename Gems/OpenXRVk/Code/Source/{InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp => OpenXRVkInteractionProfilesAsset.cpp} (70%) rename Gems/OpenXRVk/Code/Source/{OpenXRVisualizedSpacesManager.cpp => OpenXRVkVisualizedSpacesManager.cpp} (99%) rename Gems/OpenXRVk/Code/Source/{OpenXRVisualizedSpacesManager.h => OpenXRVkVisualizedSpacesManager.h} (98%) diff --git a/Gems/OpenXRVk/Code/CMakeLists.txt b/Gems/OpenXRVk/Code/CMakeLists.txt index c6a72fe26..60a842359 100644 --- a/Gems/OpenXRVk/Code/CMakeLists.txt +++ b/Gems/OpenXRVk/Code/CMakeLists.txt @@ -88,7 +88,7 @@ ly_create_alias(NAME OpenXRVk.Clients NAMESPACE Gem TARGETS Gem::OpenXRVk) ly_create_alias(NAME OpenXRVk.Unified NAMESPACE Gem TARGETS Gem::OpenXRVk) if(PAL_TRAIT_BUILD_HOST_TOOLS) - + # ly_add_target( NAME ${gem_name}.Builders.Static STATIC @@ -132,7 +132,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS) # Inject the gem name into the Module source file ly_add_source_properties( SOURCES - Source/Builders/OpenXRBuilderModule.cpp + Source/Builders/OpenXRVkBuilderModule.cpp PROPERTY COMPILE_DEFINITIONS VALUES O3DE_GEM_NAME=${gem_name} @@ -174,4 +174,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) NAME Gem::OpenXRVk.Tests ) -endif() \ No newline at end of file +endif() diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetDescriptor.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetDescriptor.h deleted file mode 100644 index 3816fee6e..000000000 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetDescriptor.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace OpenXRVk -{ - class OpenXRActionPathDescriptor final - { - public: - AZ_RTTI(OpenXRActionPathDescriptor, "{F25D6382-C9E0-414B-A542-1758F5477D03}"); - virtual ~OpenXRActionPathDescriptor() = default; - - static void Reflect(AZ::ReflectContext* reflection); - - AZStd::string GetEditorText() const; - - AZStd::string m_interactionProfileName; - AZStd::string m_userPathName; - AZStd::string m_componentPathName; - - private: - AZ::Crc32 OnInteractionProfileSelected(); - AZStd::vector GetInteractionProfiles() const; - - AZ::Crc32 OnUserPathSelected(); - AZStd::vector GetUserPaths() const; - - AZ::Crc32 OnComponentPathSelected(); - AZStd::vector GetComponentPaths() const; - }; - - class OpenXRActionDescriptor final - { - public: - AZ_RTTI(OpenXRActionDescriptor, "{90BBF6F6-C7D6-4F64-B784-CE03F86DC36B}"); - virtual ~OpenXRActionDescriptor() = default; - - static void Reflect(AZ::ReflectContext* reflection); - - AZStd::string GetEditorText() const; - - AZStd::string m_name; // Regular char* - AZStd::string m_localizedName; // UTF-8 string. - //! List of I/O action paths that will be bound to this action. - //! The first action path in this list, determines what type of action paths - //! can be added to the list. For example: - //! If the first action path happens to be a boolean, then subsequent action paths - //! can only be added if they can map to a boolean. - //! Another important case is if the this is a haptic feedback action (Output), then - //! subsequent action paths can only be of type haptic feedback actions. - AZStd::vector m_actionPathDescriptors; - }; - - class OpenXRActionSetDescriptor final - { - public: - AZ_RTTI(OpenXRActionSetDescriptor, "{3A08BC1F-656F-441F-89C3-829F95B9B329}"); - virtual ~OpenXRActionSetDescriptor() = default; - - static void Reflect(AZ::ReflectContext* reflection); - - AZStd::string GetEditorText() const; - - AZStd::string m_name; // Regular char* - AZStd::string m_localizedName; // UTF-8 string. - uint32_t m_priority = 0; // Higher values mean higher priority. - AZStd::vector m_actionDescriptors; - }; - -}// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h deleted file mode 100644 index 05bfc27a1..000000000 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfilesAsset.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -#include - - -namespace OpenXRVk -{ - //! This asset defines a list of Interaction Profile Descriptors. - //! The Engine only needs one of these assets, which is used to express - //! all the different interaction profiles (aka XR Headset Devices) that - //! are supported by OpenXR. - //! Basically this asset contains data as listed here: - //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles - class OpenXRInteractionProfilesAsset final - : public AZ::Data::AssetData - { - public: - AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAsset, AZ::SystemAllocator); - AZ_RTTI(OpenXRInteractionProfilesAsset, "{02555DCD-E363-42FB-935C-4E67CC3A1699}", AZ::Data::AssetData); - static void Reflect(AZ::ReflectContext* context); - - static constexpr char s_assetTypeName[] = "OpenXR Interaction Profiles"; - static constexpr char s_assetExtension[] = "xrprofiles"; - - //! There's only one Interaction Profile Asset that is active and defined upon engine - //! initialization. The developer can also customize which one to use via a registry property. - //! There's a default path in case the registry property is not defined by the developer. - static AZStd::string GetInteractionProfilesAssetPath(); - - const OpenXRInteractionProfileDescriptor* GetInteractionProfileDescriptor(const AZStd::string& profileName) const; - - AZStd::vector m_interactionProfileDescriptors; - }; - - //! Custom asset handler - class OpenXRInteractionProfilesAssetHandler final - : public AzFramework::GenericAssetHandler - { - public: - AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); - AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); - - static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; - - OpenXRInteractionProfilesAssetHandler(); - - bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; - }; - - -}// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h similarity index 98% rename from Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h index 828f116b5..c1a779157 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionSetsAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h @@ -10,7 +10,7 @@ #include -#include +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h similarity index 100% rename from Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRActionsInterface.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h similarity index 67% rename from Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h index e21f4a2b0..5155aeafe 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h @@ -10,12 +10,8 @@ #include -#include -#include -#include -#include -#include -#include +#include +#include namespace OpenXRVk { @@ -106,4 +102,42 @@ namespace OpenXRVk AZStd::string GetEditorText(); }; + //! This asset defines a list of Interaction Profile Descriptors. + //! The Engine only needs one of these assets, which is used to express + //! all the different interaction profiles (aka XR Headset Devices) that + //! are supported by OpenXR. + //! Basically this asset contains data as listed here: + //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#semantic-path-interaction-profiles + class OpenXRInteractionProfilesAsset final + : public AZ::Data::AssetData + { + public: + AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAsset, AZ::SystemAllocator); + AZ_RTTI(OpenXRInteractionProfilesAsset, "{02555DCD-E363-42FB-935C-4E67CC3A1699}", AZ::Data::AssetData); + static void Reflect(AZ::ReflectContext* context); + + static constexpr char s_assetTypeName[] = "OpenXR Interaction Profiles"; + static constexpr char s_assetExtension[] = "xrprofiles"; + + const OpenXRInteractionProfileDescriptor* GetInteractionProfileDescriptor(const AZStd::string& profileName) const; + + AZStd::vector m_interactionProfileDescriptors; + }; + + //! Custom asset handler + class OpenXRInteractionProfilesAssetHandler final + : public AzFramework::GenericAssetHandler + { + public: + AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); + AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); + + static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; + + OpenXRInteractionProfilesAssetHandler(); + + bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; + }; + + }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h index 5e1f0464a..01e23acf8 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h @@ -13,8 +13,8 @@ #include #include -#include -#include +#include +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkVisualizedSpacesInterface.h similarity index 100% rename from Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkVisualizedSpacesInterface.h diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp similarity index 100% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.cpp rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h similarity index 100% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRActionSetsAssetBuilder.h rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp similarity index 100% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.cpp rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h similarity index 100% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRAssetBuildersSystemComponent.h rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRBuilderModule.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp similarity index 100% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRBuilderModule.cpp rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp deleted file mode 100644 index 6ca18a266..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#include - -namespace OpenXRVk -{ - /////////////////////////////////////////////////////////// - /// OpenXRInteractionProfilesAsset - /*static*/ AZStd::string OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath() - { - //FIXME! GALIB - return "system.xrprofiles"; - } - - void OpenXRInteractionProfilesAsset::Reflect(AZ::ReflectContext* context) - { - OpenXRInteractionProfileDescriptor::Reflect(context); - - AZ::SerializeContext* serialize = azrtti_cast(context); - if (serialize) - { - serialize->Class() - ->Version(1) - ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) - ->Field("InteractionProfiles", &OpenXRInteractionProfilesAsset::m_interactionProfileDescriptors) - ; - - AZ::EditContext* edit = serialize->GetEditContext(); - if (edit) - { - edit->Class( - s_assetTypeName, "Defines the OpenXR Interaction Profiles supported by O3DE.") - ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfilesAsset::m_interactionProfileDescriptors, "Interaction Profiles", "List of interaction profile descriptors.") - ; - } - } - } - - const OpenXRInteractionProfileDescriptor* OpenXRInteractionProfilesAsset::GetInteractionProfileDescriptor(const AZStd::string& profileName) const - { - for (const auto& profileDescriptor : m_interactionProfileDescriptors) - { - if (profileName == profileDescriptor.m_uniqueName) - { - return &profileDescriptor; - } - } - return nullptr; - } - - /// OpenXRInteractionProfilesAsset - /////////////////////////////////////////////////////////// - - OpenXRInteractionProfilesAssetHandler::OpenXRInteractionProfilesAssetHandler() - : AzFramework::GenericAssetHandler( - OpenXRInteractionProfilesAsset::s_assetTypeName, - "Other", - OpenXRInteractionProfilesAsset::s_assetExtension) - { - } - - bool OpenXRInteractionProfilesAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) - { - auto profileAsset = asset.GetAs(); - if (!profileAsset) - { - AZ_Error(LogName, false, "This should be an OpenXR Interaction Profile Asset, as this is the only type this handler can process."); - return false; - } - const auto& descriptorsList = profileAsset->m_interactionProfileDescriptors; - if (descriptorsList.empty()) - { - AZ_Error(LogName, false, "The list of Interaction Profile Descriptors is empty."); - return false; - } - - if (!m_serializeContext) - { - AZ_Error(LogName, false, "Can't save the OpenXR Interaction Profile Asset without a serialize context."); - return false; - } - - for (const auto& profileDescriptor : descriptorsList) - { - if (!profileDescriptor.Validate()) - { - return false; - } - } - return AZ::Utils::SaveObjectToStream(*stream, AZ::ObjectStream::ST_JSON, profileAsset, - asset->RTTI_GetType(), m_serializeContext); - } - -} // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h deleted file mode 100644 index 95e4b97ca..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include - -#include -#include -#include - -#include - -namespace OpenXRVk -{ - class IOpenXRInteractionProfilesProvider - { - public: - AZ_RTTI(IOpenXRInteractionProfilesProvider, "{25598F43-3634-4B2D-9A87-95E8BFDAB673}"); - AZ_DISABLE_COPY_MOVE(IOpenXRInteractionProfilesProvider); - - IOpenXRInteractionProfilesProvider() = default; - virtual ~IOpenXRInteractionProfilesProvider() = default; - - virtual const AZStd::vector& GetInteractionProfileNames() const = 0; - - virtual const OpenXRInteractionProfileDescriptor * GetInteractionProfileDescriptor(const AZStd::string profileName) const = 0; - - // ////////////////////////////////////////////////////////// - // // The following functions are called during asset creation time. - // virtual AZStd::string GetName() const = 0; - // virtual AZStd::vector GetUserPaths() const = 0; - // virtual AZStd::string GetUserTopPath(const AZStd::string& userPathName) const = 0; - // virtual AZStd::vector GetComponentPaths(const AZStd::string& userPath) const = 0; - // // - // ////////////////////////////////////////////////////////// - // - // - // //////////////////////////////////////////////////////////// - // // The following functions are called at runtime. - // // - // struct ActionPathInfo - // { - // //! Absolute path looks like: - // //! /user/hand/left/input/select/click - // AZStd::string m_absolutePath; - // XrActionType m_actionType = XrActionType::XR_ACTION_TYPE_MAX_ENUM; - // }; - // - // //! @param userPath Typically comes from OpenXRActionPath::m_userPath. - // //! @param componentPath Typically comes from OpenXRActionPath::m_componentPath. - // virtual ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const = 0; - // - // //! @returns An interaction profile/provider string path that looks like: - // //! "/interaction_profiles/khr/simple_controller" - // //! "/interaction_profiles/oculus/touch_controller" - // virtual AZStd::string GetInteractionProviderPath() const = 0; - // // - // ///////////////////////////////////////////////////////////// - }; - - using OpenXRInteractionProfilesProviderInterface = AZ::Interface; -} diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp deleted file mode 100644 index 0ae07c23d..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - - -#include -#include - -#include "OpenXRInteractionProfilesProviderSystemComponent.h" - -namespace OpenXRVk -{ - void OpenXRInteractionProfilesProviderSystemComponent::Reflect(AZ::ReflectContext* context) - { - if (auto serializeContext = azrtti_cast(context)) - { - serializeContext->Class() - ->Version(1); - } - } - - void OpenXRInteractionProfilesProviderSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) - { - provided.push_back(AZ_CRC_CE("InteractionProfileProviderService")); - } - - void OpenXRInteractionProfilesProviderSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) - { - incompatible.push_back(AZ_CRC("InteractionProfileProviderService")); - } - - void OpenXRInteractionProfilesProviderSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) - { - (void)required; - } - - void OpenXRInteractionProfilesProviderSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) - { - dependent.push_back(AZ_CRC("AssetCatalogService")); - } - - void OpenXRInteractionProfilesProviderSystemComponent::Activate() - { - // m_name = { - // "Khronos Simple", // Khronos Simple Interaction Profile - // "/interaction_profiles/khr/simple_controller" - // }; - // - // m_userPaths = { - // {LeftHand, "/user/hand/left"}, - // {RightHand, "/user/hand/right"} - // }; - // - // const AZStd::vector commonPaths = { - // {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - // {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - // {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - // {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - // {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, - // }; - - // m_name = { - // "Oculus Touch", - // "/interaction_profiles/oculus/touch_controller" - // }; - // - // m_userPaths = { - // {LeftHand, "/user/hand/left"}, - // {RightHand, "/user/hand/right"} - // }; - // - // const AZStd::vector commonPaths = { - // {{"X Click", "/input/x/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - // {{"X Touch", "/input/x/touch"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - // {{"Select Button", "/input/select/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, // Just Testing. NOT supported in oculus - // {{"Menu Button", "/input/menu/click"}, XR_ACTION_TYPE_BOOLEAN_INPUT}, - // {{"Grip Pose", "/input/grip/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - // {{"Aim Pose", "/input/aim/pose"}, XR_ACTION_TYPE_POSE_INPUT}, - // {{"Vibration", "/output/haptic"}, XR_ACTION_TYPE_VIBRATION_OUTPUT}, - // }; - // m_componentPaths[LeftHand] = commonPaths; - // m_componentPaths[RightHand] = commonPaths; - - AzFramework::AssetCatalogEventBus::Handler::BusConnect(); - } - - void OpenXRInteractionProfilesProviderSystemComponent::Deactivate() - { - if (AzFramework::AssetCatalogEventBus::Handler::BusIsConnected()) - { - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); - } - - if (AZ::Data::AssetBus::Handler::BusIsConnected()) - { - AZ::Data::AssetBus::Handler::BusDisconnect(); - } - } - - AZStd::string OpenXRInteractionProfilesProviderSystemComponent::GetDefaultInteractionProfilesAssetPath() - { - // TODO: Make this user configurable with registry property. - return "system.xrprofiles"; - } - - ////////////////////////////////////////////////////////////////////////// - // AssetCatalogEventBus - void OpenXRInteractionProfilesProviderSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/) - { - // No need to staying connected anymore. - AzFramework::AssetCatalogEventBus::Handler::BusDisconnect(); - - // Load, asychronously, the asset that contains the list of supported - // OpenXR Interaction Profiles. - constexpr bool AutoGenerateId = false; - AZ::Data::AssetId assetId; - const auto assetPath = GetDefaultInteractionProfilesAssetPath(); - AZ::Data::AssetCatalogRequestBus::BroadcastResult( - assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, - assetPath.c_str(), azrtti_typeid(), AutoGenerateId); - if (!assetId.IsValid()) - { - AZ_Assert(false, "Failed to find the interaction profiles asset with path [%s]", assetPath.c_str()); - AZ_Error(LogName, false, "Failed to find the interaction profiles asset with path [%s]", assetPath.c_str()); - return; - } - - AZ::Data::AssetBus::Handler::BusConnect(assetId); - - m_interactionProfilesAsset = AZ::Data::AssetManager::Instance().GetAsset(assetId, AZ::Data::AssetLoadBehavior::QueueLoad); - - } - ////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////// - // AZ::Data::AssetBus::Handler overrides - void OpenXRInteractionProfilesProviderSystemComponent::OnAssetReady(AZ::Data::Asset asset) - { - AZ::Data::AssetBus::Handler::BusDisconnect(); - m_interactionProfilesAsset = asset; - } - - void OpenXRInteractionProfilesProviderSystemComponent::OnAssetError(AZ::Data::Asset asset) - { - AZ::Data::AssetBus::Handler::BusDisconnect(); - m_interactionProfilesAsset = {}; - AZ_Assert(false, "Failed to load interaction profiles asset"); - AZ_Error(LogName, false, "Failed to load interaction profiles asset"); - - } - ////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////// - // OpenXRInteractionProfileBus::Handler overrides - const AZStd::vector& OpenXRInteractionProfilesProviderSystemComponent::GetInteractionProfileNames() const - { - static AZStd::vector profileNames; - - if (!m_interactionProfilesAsset.IsReady()) - { - AZ_Error(LogName, false, "Interaction Profiles Asset Doesn't exist or it is not ready yet."); - return profileNames; - } - - if (profileNames.empty()) - { - for (const auto& profileDescriptor : m_interactionProfilesAsset->m_interactionProfileDescriptors) - { - profileNames.push_back(profileDescriptor.m_uniqueName); - } - } - - return profileNames; - } - - const OpenXRInteractionProfileDescriptor* OpenXRInteractionProfilesProviderSystemComponent::GetInteractionProfileDescriptor(const AZStd::string profileName) const - { - if (!m_interactionProfilesAsset.IsReady()) - { - AZ_Error(LogName, false, "Interaction Profiles Asset Doesn't exist or it is not ready yet."); - return nullptr; - } - - for (const auto& profileDescriptor : m_interactionProfilesAsset->m_interactionProfileDescriptors) - { - if (profileName == profileDescriptor.m_uniqueName) - { - return &profileDescriptor; - } - } - - AZ_Error(LogName, false, "Interaction Profile with name [%s] doesn't exist.", profileName.c_str()); - return nullptr; - } - - // AZStd::string KHRSimpleProfileSystemComponent::GetName() const - // { - // return m_name.m_displayName; - // } - // - // AZStd::vector KHRSimpleProfileSystemComponent::GetUserPaths() const - // { - // AZStd::vector retList; - // retList.reserve(m_userPaths.size()); - // for (const auto& pathTuple : m_userPaths) - // { - // retList.push_back(pathTuple.m_displayName); - // } - // return retList; - // } - // - // AZStd::string KHRSimpleProfileSystemComponent::GetUserTopPath(const AZStd::string& userPathName) const - // { - // for (const auto& openxrPath : m_userPaths) - // { - // if (openxrPath.m_displayName == userPathName) - // { - // return openxrPath.m_xrRelativePath; - // } - // } - // return AZStd::string(""); - // } - // - // AZStd::vector KHRSimpleProfileSystemComponent::GetComponentPaths(const AZStd::string& userPath) const - // { - // if (!m_componentPaths.contains(userPath)) - // { - // AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); - // return {}; - // } - // - // const auto& paths = m_componentPaths.at(userPath); - // AZStd::vector retList; - // retList.reserve(paths.size()); - // for (const auto& pathTuple : paths) - // { - // retList.push_back(pathTuple.m_displayName); - // } - // return retList; - // } - // - // OpenXRInteractionProfile::ActionPathInfo KHRSimpleProfileSystemComponent::GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const - // { - // OpenXRInteractionProfile::ActionPathInfo retPathInfo; - // - // const auto* openxrUserPath = AZStd::find_if(m_userPaths.begin(), m_userPaths.end(), - // [userPath](const OpenXRPath& entry) { - // return entry.m_displayName == userPath; - // }); - // - // if (openxrUserPath == m_userPaths.end()) - // { - // AZ_Error(LogName, false, "Invalid user path [%s].\n", userPath.c_str()); - // return retPathInfo; - // } - // - // const auto& componentPaths = m_componentPaths.at(userPath); - // const auto* openxrComponentPath = AZStd::find_if(componentPaths.begin(), componentPaths.end(), - // [componentPath](const OpenXRComponentPath& entry) { - // return entry.m_displayName == componentPath; - // }); - // - // if (openxrComponentPath == componentPaths.end()) - // { - // AZ_Error(LogName, false, "Invalid component path [%s] for user path [%s].\n", componentPath.c_str(), userPath.c_str()); - // return retPathInfo; - // } - // - // retPathInfo.m_actionType = openxrComponentPath->m_actionType; - // retPathInfo.m_absolutePath = openxrUserPath->m_xrRelativePath + openxrComponentPath->m_xrRelativePath; - // - // return retPathInfo; - // } - // - // AZStd::string KHRSimpleProfileSystemComponent::GetInteractionProviderPath() const - // { - // return m_name.m_xrRelativePath; - // } - /////////////////////////////////////////////////////////////////// -} //namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h b/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h deleted file mode 100644 index 6ac18d31e..000000000 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) Contributors to the Open 3D Engine Project. - * For complete copyright and license terms please see the LICENSE at the root of this distribution. - * - * SPDX-License-Identifier: Apache-2.0 OR MIT - * - */ - -#pragma once - -#include -#include - -#include - -#include -#include "OpenXRInteractionProfilesProviderInterface.h" - - -namespace OpenXRVk -{ - struct OpenXRPath - { - AZStd::string m_displayName; - //! Although this path is relative, - //! it should start with "/". - //! Examples: - //! 1- For a User Path this string would look like this: - //! "/user/hand/left" - //! 2- For a Component Path this string would look like this: - //! "/input/select/click" - AZStd::string m_xrRelativePath; - }; - - struct OpenXRComponentPath : public OpenXRPath - { - XrActionType m_actionType; - }; - - //! This system component provides data that can be used to pick xrActions - //! that will used at runtime for any given application. - class OpenXRInteractionProfilesProviderSystemComponent final - : public AZ::Component - , private AzFramework::AssetCatalogEventBus::Handler - , private AZ::Data::AssetBus::Handler - , public OpenXRInteractionProfilesProviderInterface::Registrar - { - public: - AZ_COMPONENT(OpenXRInteractionProfilesProviderSystemComponent, "{123EDAF5-416B-4AEF-BEEC-03A8A8C71643}"); - - static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); - static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); - static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); - static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); - - static void Reflect(AZ::ReflectContext* context); - - OpenXRInteractionProfilesProviderSystemComponent() = default; - ~OpenXRInteractionProfilesProviderSystemComponent() = default; - - ////////////////////////////////////////////////////////////////////////// - // Component - void Activate() override; - void Deactivate() override; - ////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////// - // AssetCatalogEventBus - void OnCatalogLoaded(const char* /*catalogFile*/) override; - ////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////////// - // AZ::Data::AssetBus::Handler overrides - void OnAssetReady(AZ::Data::Asset asset) override; - void OnAssetError(AZ::Data::Asset asset) override; - ////////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////// - // OpenXRInteractionProfileBus::Handler overrides - const AZStd::vector& GetInteractionProfileNames() const override; - const OpenXRInteractionProfileDescriptor* GetInteractionProfileDescriptor(const AZStd::string profileName) const override; - - // AZStd::string GetName() const override; - // AZStd::vector GetUserPaths() const override; - // AZStd::string GetUserTopPath(const AZStd::string& userPathName) const override; - // AZStd::vector GetComponentPaths(const AZStd::string& userPath) const override; - // OpenXRInteractionProfile::ActionPathInfo GetActionPathInfo(const AZStd::string& userPath, const AZStd::string& componentPath) const override; - // AZStd::string GetInteractionProviderPath() const override; - - /////////////////////////////////////////////////////////////////// - - private: - - AZStd::string GetDefaultInteractionProfilesAssetPath(); - - static constexpr char LogName[] = "OpenXRInteractionProfilesProviderSystemComponent"; - - AZ::Data::Asset m_interactionProfilesAsset; - - }; -}//namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetDescriptor.cpp similarity index 100% rename from Gems/OpenXRVk/Code/Source/OpenXRActionSetDescriptor.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRVkActionSetDescriptor.cpp diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp similarity index 93% rename from Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp index 0d18e7da7..05c971384 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionSetsAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp @@ -13,32 +13,11 @@ //! The error is triggered when calling ->Field("InteractionProfilesAsset", &OpenXRActionSetsAsset::m_interactionProfilesAsset) #include -#include +#include namespace OpenXRVk { - // // This function is only used when an OpenXRActionSetsAsset is being created/edited with the AssetEditor. - // static const AZ::Data::Asset& GetInteractionProfilesAsset() - // { - // static AZ::Data::Asset s_asset; - // if (!s_asset.IsReady()) - // { - // const auto interactionProfilesAssetPath = OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath(); - // if (interactionProfilesAssetPath.empty()) - // { - // AZ_Error("OpenXRActionPathDescriptor", false, "No interaction profile asset has been defined"); - // return s_asset; - // } - // s_asset = AZ::RPI::AssetUtils::LoadCriticalAsset(interactionProfilesAssetPath); - // if (!s_asset.IsReady()) - // { - // AZ_Error("OpenXRActionPathDescriptor", false, "The system interaction profiles asset [%s] is not ready. There's no data available to create/edit an OpenXRActionSetsAsset", - // interactionProfilesAssetPath.c_str()); - // return s_asset; - // } - // } - // return s_asset; - // } + namespace EditorInternal { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp similarity index 99% rename from Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp index e15168997..949ffce6d 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp @@ -14,8 +14,8 @@ //#include #include -#include -#include "OpenXRActionsManager.h" +#include +#include "OpenXRVkActionsManager.h" namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h similarity index 98% rename from Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h rename to Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h index e94b3bf6d..d32d9f357 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h @@ -15,8 +15,8 @@ #include -#include -#include +#include +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp similarity index 98% rename from Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp index 0685b8fe5..583df09c6 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp @@ -6,11 +6,9 @@ * */ -#include "OpenXRBehaviorReflection.h" +#include -// #include - -#include +#include "OpenXRVkBehaviorReflection.h" namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.h b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.h similarity index 100% rename from Gems/OpenXRVk/Code/Source/OpenXRBehaviorReflection.h rename to Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.h diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp index e0c7c2bd2..548271061 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp similarity index 70% rename from Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp index 3883a531c..4f1437feb 100644 --- a/Gems/OpenXRVk/Code/Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp @@ -6,9 +6,7 @@ * */ -#include - -#include +#include namespace OpenXRVk { @@ -73,13 +71,13 @@ namespace OpenXRVk { edit->Class("Component Path", "An OpenXR Component Path that is supported by an OpenXR User Path") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionComponentPathDescriptor::GetEditorText) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionComponentPathDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionComponentPathDescriptor::m_name, "Name", "User friendly name.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionComponentPathDescriptor::m_path, "Path", "An OpenXR Path string that starts with '/' BUT is relative to a User Path.") ->DataElement(AZ::Edit::UIHandlers::ComboBox, &OpenXRInteractionComponentPathDescriptor::m_actionTypeStr, "Action Type", "Data type of this action.") - ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorXrActionTypeNames) + ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorXrActionTypeNames) ; } } @@ -113,10 +111,10 @@ namespace OpenXRVk { edit->Class("User Path", "Represents a User Path supported by an Interaction Profile") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionUserPathDescriptor::GetEditorText) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionUserPathDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_name, "Name", "User friendly name.") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_path, "Path", "An OpenXR Path string that starts with '/'.") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionUserPathDescriptor::m_componentPathDescriptors, "Component Paths", "List of component paths supported by this User Path") ; @@ -162,17 +160,17 @@ namespace OpenXRVk ->Field("UserPathDescriptors", &OpenXRInteractionProfileDescriptor::m_userPathDescriptors) ->Field("CommonComponentPathDescriptors", &OpenXRInteractionProfileDescriptor::m_commonComponentPathDescriptors) ; - + AZ::EditContext* edit = serialize->GetEditContext(); if (edit) { edit->Class( "Interaction Profile", "Defines an OpenXR Interaction Profile Supported by O3DE.") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") - ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionProfileDescriptor::GetEditorText) - ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionProfileDescriptor::GetEditorText) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Unique Name", "Unique name across all interaction profiles") - ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) + ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_path, "Path", "OpenXR Canonical Path for this interation profile.") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_userPathDescriptors, "User Paths", "List of user paths") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_commonComponentPathDescriptors, "Common Component Paths", "List of component paths supported by all User Paths") @@ -253,4 +251,88 @@ namespace OpenXRVk /// OpenXRInteractionProfileDescriptor /////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////// + /// OpenXRInteractionProfilesAsset + void OpenXRInteractionProfilesAsset::Reflect(AZ::ReflectContext* context) + { + OpenXRInteractionProfileDescriptor::Reflect(context); + + AZ::SerializeContext* serialize = azrtti_cast(context); + if (serialize) + { + serialize->Class() + ->Version(1) + ->Attribute(AZ::Edit::Attributes::EnableForAssetEditor, true) + ->Field("InteractionProfiles", &OpenXRInteractionProfilesAsset::m_interactionProfileDescriptors) + ; + + AZ::EditContext* edit = serialize->GetEditContext(); + if (edit) + { + edit->Class( + s_assetTypeName, "Defines the OpenXR Interaction Profiles supported by O3DE.") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfilesAsset::m_interactionProfileDescriptors, "Interaction Profiles", "List of interaction profile descriptors.") + ; + } + } + } + + const OpenXRInteractionProfileDescriptor* OpenXRInteractionProfilesAsset::GetInteractionProfileDescriptor(const AZStd::string& profileName) const + { + for (const auto& profileDescriptor : m_interactionProfileDescriptors) + { + if (profileName == profileDescriptor.m_uniqueName) + { + return &profileDescriptor; + } + } + return nullptr; + } + + /// OpenXRInteractionProfilesAsset + /////////////////////////////////////////////////////////// + + OpenXRInteractionProfilesAssetHandler::OpenXRInteractionProfilesAssetHandler() + : AzFramework::GenericAssetHandler( + OpenXRInteractionProfilesAsset::s_assetTypeName, + "Other", + OpenXRInteractionProfilesAsset::s_assetExtension) + { + } + + bool OpenXRInteractionProfilesAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) + { + auto profileAsset = asset.GetAs(); + if (!profileAsset) + { + AZ_Error(LogName, false, "This should be an OpenXR Interaction Profile Asset, as this is the only type this handler can process."); + return false; + } + const auto& descriptorsList = profileAsset->m_interactionProfileDescriptors; + if (descriptorsList.empty()) + { + AZ_Error(LogName, false, "The list of Interaction Profile Descriptors is empty."); + return false; + } + + if (!m_serializeContext) + { + AZ_Error(LogName, false, "Can't save the OpenXR Interaction Profile Asset without a serialize context."); + return false; + } + + for (const auto& profileDescriptor : descriptorsList) + { + if (!profileDescriptor.Validate()) + { + return false; + } + } + return AZ::Utils::SaveObjectToStream(*stream, AZ::ObjectStream::ST_JSON, profileAsset, + asset->RTTI_GetType(), m_serializeContext); + } + } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp index f73f7e845..e4e1bec9b 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp @@ -11,7 +11,6 @@ #include #include -#include "InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h" namespace OpenXRVk { @@ -27,7 +26,6 @@ namespace OpenXRVk : AZ::Module() { m_descriptors.insert(m_descriptors.end(), { - OpenXRInteractionProfilesProviderSystemComponent::CreateDescriptor(), SystemComponent::CreateDescriptor(), XRCameraMovementComponent::CreateDescriptor(), //KHRSimpleProfileSystemComponent::CreateDescriptor(), @@ -38,7 +36,6 @@ namespace OpenXRVk { return { - azrtti_typeid(), azrtti_typeid(), }; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index caebfea1d..79c97b652 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -21,8 +21,8 @@ #include #include -#include "OpenXRVisualizedSpacesManager.h" -#include "OpenXRActionsManager.h" +#include "OpenXRVkVisualizedSpacesManager.h" +#include "OpenXRVkActionsManager.h" namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index 72488006c..5bf42b73d 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -19,7 +19,7 @@ #include #include -#include "OpenXRBehaviorReflection.h" +#include "OpenXRVkBehaviorReflection.h" #include diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.cpp similarity index 99% rename from Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.cpp index 5b53f88b2..a8f8b3ded 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.cpp @@ -11,7 +11,7 @@ #include -#include "OpenXRVisualizedSpacesManager.h" +#include "OpenXRVkVisualizedSpacesManager.h" namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.h similarity index 98% rename from Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h rename to Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.h index c83cb437f..625f1f388 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVisualizedSpacesManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.h @@ -10,7 +10,7 @@ #include -#include +#include namespace OpenXRVk { diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp index c1672a354..5b3c009cd 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp @@ -16,7 +16,7 @@ #include // GALIB #include -#include +#include #include #include diff --git a/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake index 47ef6efed..b26c1ab73 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake @@ -7,8 +7,8 @@ # set(FILES - Source/Builders/OpenXRActionSetsAssetBuilder.cpp - Source/Builders/OpenXRActionSetsAssetBuilder.h - Source/Builders/OpenXRAssetBuildersSystemComponent.cpp - Source/Builders/OpenXRAssetBuildersSystemComponent.h + Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp + Source/Builders/OpenXRVkActionSetsAssetBuilder.h + Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp + Source/Builders/OpenXRVkAssetBuildersSystemComponent.h ) diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index a7a47450b..c728cfc71 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -17,11 +17,10 @@ set(FILES Include/OpenXRVk/OpenXRVkSwapChain.h Include/OpenXRVk/OpenXRVkSystemComponent.h Include/OpenXRVk/OpenXRVkUtils.h - Include/OpenXRVk/OpenXRActionsInterface.h - Include/OpenXRVk/OpenXRVisualizedSpacesInterface.h - Include/OpenXRVk/OpenXRInteractionProfileDescriptor.h - Include/OpenXRVk/OpenXRInteractionProfilesAsset.h - Include/OpenXRVk/OpenXRActionSetsAsset.h + Include/OpenXRVk/OpenXRVkActionsInterface.h + Include/OpenXRVk/OpenXRVkVisualizedSpacesInterface.h + Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h + Include/OpenXRVk/OpenXRVkActionSetsAsset.h Source/InputDeviceXRController.cpp Source/OpenXRVkCommon.h Source/OpenXRVkDevice.cpp @@ -33,18 +32,14 @@ set(FILES Source/OpenXRVkSwapChain.cpp Source/OpenXRVkSystemComponent.cpp Source/OpenXRVkUtils.cpp - Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.cpp - Source/InteractionProfiles/OpenXRInteractionProfilesProviderSystemComponent.h - Source/InteractionProfiles/OpenXRInteractionProfileDescriptor.cpp - Source/InteractionProfiles/OpenXRInteractionProfilesAsset.cpp - Source/InteractionProfiles/OpenXRInteractionProfilesProviderInterface.h - Source/OpenXRActionSetsAsset.cpp + Source/OpenXRVkInteractionProfilesAsset.cpp + Source/OpenXRVkActionSetsAsset.cpp Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h - Source/OpenXRVisualizedSpacesManager.cpp - Source/OpenXRVisualizedSpacesManager.h - Source/OpenXRActionsManager.cpp - Source/OpenXRActionsManager.h - Source/OpenXRBehaviorReflection.cpp - Source/OpenXRBehaviorReflection.h + Source/OpenXRVkVisualizedSpacesManager.cpp + Source/OpenXRVkVisualizedSpacesManager.h + Source/OpenXRVkActionsManager.cpp + Source/OpenXRVkActionsManager.h + Source/OpenXRVkBehaviorReflection.cpp + Source/OpenXRVkBehaviorReflection.h ) diff --git a/Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake b/Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake index 02da43db7..5b2c79f13 100644 --- a/Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_shared_builder_files.cmake @@ -7,5 +7,5 @@ # set(FILES - Source/Builders/OpenXRBuilderModule.cpp + Source/Builders/OpenXRVkBuilderModule.cpp ) From 1faabebc43dc2d90c7d3356c17d192fe7a6fe414 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:50:19 -0600 Subject: [PATCH 19/33] Working with action sets, and actions with Lua works fine. Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkActionsInterface.h | 21 +- .../Code/Source/OpenXRVkActionsManager.cpp | 98 +++----- .../Code/Source/OpenXRVkActionsManager.h | 11 +- .../Source/OpenXRVkBehaviorReflection.cpp | 214 +++++++++++++++++- .../Code/Source/XRCameraMovementComponent.cpp | 65 ++++-- 5 files changed, 301 insertions(+), 108 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h index 2482a0a80..aa91db92d 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h @@ -16,8 +16,11 @@ namespace OpenXRVk { - struct PoseWithVelocities + struct PoseWithVelocities final { + AZ_TYPE_INFO(PoseWithVelocities, "{AF5B9FF7-FB02-4DA4-89FB-66E605F728E2}"); + static void Reflect(AZ::ReflectContext* context); + AZ::Transform m_pose; AZ::Vector3 m_linearVelocity; AZ::Vector3 m_angularVelocity; @@ -40,12 +43,18 @@ namespace OpenXRVk using ActionHandle = AZ::RHI::Handle; virtual AZStd::vector GetAllActionSets() const = 0; - virtual AZStd::vector GetActiveActionSets() const = 0; - virtual AZStd::vector GetInactiveActionSets() const = 0; - virtual AZ::Outcome ChangeActionSetState(const AZStd::string& actionSetName, bool activate) = 0; - virtual AZ::Outcome ChangeActionSetsState(const AZStd::vector& actionSetNames, bool activate) = 0; - virtual ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const = 0; + // @returns: + // If successful: + // - true means the action set exists and is active. + // - false means the action set exists and is inactive. + virtual AZ::Outcome GetActionSetState(const AZStd::string& actionSetName) const = 0; + // @returns: + // if successful: + // returns the current state of the action set before changing it. + virtual AZ::Outcome SetActionSetState(const AZStd::string& actionSetName, bool activate) = 0; + + virtual AZ::Outcome GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const = 0; virtual AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) const = 0; virtual AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const = 0; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp index 949ffce6d..9a6b9ee6a 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp @@ -482,61 +482,42 @@ namespace OpenXRVk return retList; } - - AZStd::vector ActionsManager::GetActiveActionSets() const + AZ::Outcome ActionsManager::GetActionSetState(const AZStd::string& actionSetName) const { - AZStd::vector retList; - retList.reserve(m_activeActionSets.count()); for (size_t i = 0; i < m_actionSets.size(); i++) { - if (m_activeActionSets[i]) + if (m_actionSets[i].m_name == actionSetName) { - retList.push_back(m_actionSets[i].m_name); + return AZ::Success(m_activeActionSets[i]); } } - return retList; + return AZ::Failure( + AZStd::string::format("ActionSet with name [%s] not found.", actionSetName.c_str()) + ); } - - AZStd::vector ActionsManager::GetInactiveActionSets() const + AZ::Outcome ActionsManager::SetActionSetState(const AZStd::string& actionSetName, bool activate) { - AZStd::vector retList; - retList.reserve(m_actionSets.size() - m_activeActionSets.count()); for (size_t i = 0; i < m_actionSets.size(); i++) { - if (!m_activeActionSets[i]) + if (m_actionSets[i].m_name == actionSetName) { - retList.push_back(m_actionSets[i].m_name); + bool prevState = m_activeActionSets[i]; + if (prevState != activate) + { + m_activeActionSets[i] = activate; + RecreateXrActiveActionSets(); + } + return AZ::Success(prevState); } } - return retList; + return AZ::Failure( + AZStd::string::format("ActionSet with name [%s] not found.", actionSetName.c_str()) + ); } - AZ::Outcome ActionsManager::ChangeActionSetState(const AZStd::string& actionSetName, bool activate) - { - constexpr bool recreateXrActiveActionSets = true; - return ChangeActionSetStateInternal(actionSetName, activate, recreateXrActiveActionSets); - } - - - AZ::Outcome ActionsManager::ChangeActionSetsState(const AZStd::vector& actionSetNames, bool activate) - { - constexpr bool recreateXrActiveActionSets = false; - for (const auto& actionSetName : actionSetNames) - { - auto outcome = ChangeActionSetStateInternal(actionSetName, activate, recreateXrActiveActionSets); - if (!outcome.IsSuccess()) - { - return outcome; - } - } - RecreateXrActiveActionSets(); - return AZ::Success(true); - } - - - IOpenXRActions::ActionHandle ActionsManager::GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const + AZ::Outcome ActionsManager::GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const { for (const auto& actionSetInfo : m_actionSets) { @@ -547,11 +528,16 @@ namespace OpenXRVk const auto itor = actionSetInfo.m_actions.find(actionName); if (itor == actionSetInfo.m_actions.end()) { - return IOpenXRActions::ActionHandle::Null; + return AZ::Failure( + AZStd::string::format("ActionSet with name [%s] does not have an action named [%].", + actionSetName.c_str(), actionName.c_str()) + ); } - return itor->second; + return AZ::Success(itor->second); } - return IOpenXRActions::ActionHandle::Null; + return AZ::Failure( + AZStd::string::format("ActionSet with name [%s] not found.", actionSetName.c_str()) + ); } AZ::Outcome ActionsManager::GetActionStateBoolean(ActionHandle actionHandle) const @@ -804,36 +790,6 @@ namespace OpenXRVk ///////////////////////////////////////////////// - AZ::Outcome ActionsManager::ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets) - { - // First get the index. - size_t foundIdx = 0; - for (const auto& actionSetInfo : m_actionSets) - { - if (actionSetInfo.m_name == actionSetName) - { - break; - } - foundIdx++; - } - - if (foundIdx >= m_actionSets.size()) - { - return AZ::Failure(AZStd::string::format( - "ActionSet with name [%s] not found.", actionSetName.c_str())); - } - - m_activeActionSets.set(foundIdx, activate); - - if (recreateXrActiveActionSets) - { - RecreateXrActiveActionSets(); - } - - return AZ::Success(true); - } - - void ActionsManager::RecreateXrActiveActionSets() { // Recreate and cache the XrActionActionSet list. diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h index d32d9f357..d9cebeedd 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h @@ -49,12 +49,10 @@ namespace OpenXRVk ///////////////////////////////////////////////// /// OpenXRActionsInterface overrides AZStd::vector GetAllActionSets() const override; - AZStd::vector GetActiveActionSets() const override; - AZStd::vector GetInactiveActionSets() const override; - AZ::Outcome ChangeActionSetState(const AZStd::string& actionSetName, bool activate) override; - AZ::Outcome ChangeActionSetsState(const AZStd::vector& actionSetNames, bool activate) override; + AZ::Outcome GetActionSetState(const AZStd::string& actionSetName) const override; + AZ::Outcome SetActionSetState(const AZStd::string& actionSetName, bool activate) override; - ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const override; + AZ::Outcome GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const override; AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) const override; AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const override; @@ -135,7 +133,6 @@ namespace OpenXRVk SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const; - AZ::Outcome ChangeActionSetStateInternal(const AZStd::string& actionSetName, bool activate, bool recreateXrActiveActionSets = false); void RecreateXrActiveActionSets(); @@ -178,7 +175,9 @@ namespace OpenXRVk // the following lists are updated: //! Each bit is an index in @m_actionsSets AZStd::bitset m_activeActionSets; + //! Here we cache the list of OpenXR native handles for active action sets. + //! This list is recreated each time RecreateXrActiveActionSets() is called. AZStd::vector m_xrActiveActionSets; }; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp index 583df09c6..ad52d8783 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp @@ -7,11 +7,36 @@ */ #include +#include #include "OpenXRVkBehaviorReflection.h" namespace OpenXRVk { + void PoseWithVelocities::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serializeContext = azrtti_cast(context)) + { + serializeContext->Class() + ->Version(1) + ->Field("Pose", &PoseWithVelocities::m_pose) + ->Field("LinearVelocity", &PoseWithVelocities::m_linearVelocity) + ->Field("AngularVelocitu", &PoseWithVelocities::m_angularVelocity) + ; + } + + if (AZ::BehaviorContext* behaviorContext = azrtti_cast(context)) + { + behaviorContext->Class() + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "openxr") + ->Property("pose", BehaviorValueGetter(&PoseWithVelocities::m_pose), nullptr) + ->Property("linearVelocity", BehaviorValueGetter(&PoseWithVelocities::m_linearVelocity), nullptr) + ->Property("angularVelocity", BehaviorValueGetter(&PoseWithVelocities::m_angularVelocity), nullptr) + ; + } + } + class OpenXRVisualizedSpaces { public: @@ -164,10 +189,179 @@ namespace OpenXRVk iface->ForceViewPosesCacheUpdate(); } - }; + }; // class OpenXRVisualizedSpaces + + class OpenXRActions + { + public: + AZ_TYPE_INFO(OpenXRActions, "{290DF9D5-4042-4843-BF23-D49F9FAD6D90}"); + AZ_CLASS_ALLOCATOR(OpenXRActions, AZ::SystemAllocator); + + static constexpr char LogName[] = "OpenXRActions"; + + OpenXRActions() = default; + ~OpenXRActions() = default; + + static AZStd::vector GetAllActionSets() + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRActionsInterface is not available.", __FUNCTION__); + return {}; + } + return iface->GetAllActionSets(); + } + + static AZ::Outcome GetActionSetState(const AZStd::string& actionSetName) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRActionsInterface is not available.", __FUNCTION__); + return {}; + } + return iface->GetActionSetState(actionSetName); + } + + static AZ::Outcome SetActionSetState(const AZStd::string& actionSetName, bool activate) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRActionsInterface is not available.", __FUNCTION__); + return {}; + } + return iface->SetActionSetState(actionSetName, activate); + } + + static AZ::Outcome GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->GetActionHandle(actionSetName, actionName); + } + + static AZ::Outcome GetActionStateBoolean(IOpenXRActions::ActionHandle actionHandle) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->GetActionStateBoolean(actionHandle); + } + + static AZ::Outcome GetActionStateFloat(IOpenXRActions::ActionHandle actionHandle) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->GetActionStateFloat(actionHandle); + } + + static AZ::Outcome GetActionStateVector2(IOpenXRActions::ActionHandle actionHandle) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->GetActionStateVector2(actionHandle); + } + + static AZ::Outcome SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->SetBaseVisualizedSpaceForPoseActions(visualizedSpaceName); + } + + static const AZStd::string& GetBaseVisualizedSpaceForPoseActions() + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + AZ_Error(LogName, false, "%s: OpenXRActionsInterface is not available.", __FUNCTION__); + static const AZStd::string emptyStr; + return emptyStr; + } + return iface->GetBaseVisualizedSpaceForPoseActions(); + } + + static AZ::Outcome GetActionStatePose(IOpenXRActions::ActionHandle actionHandle) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->GetActionStatePose(actionHandle); + } + + static AZ::Outcome GetActionStatePoseWithVelocities(IOpenXRActions::ActionHandle actionHandle) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->GetActionStatePoseWithVelocities(actionHandle); + } + + static AZ::Outcome ApplyHapticVibrationAction(IOpenXRActions::ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->ApplyHapticVibrationAction(actionHandle, durationNanos, frequencyHz, amplitude); + } + + static AZ::Outcome StopHapticVibrationAction(IOpenXRActions::ActionHandle actionHandle) + { + const auto iface = OpenXRActionsInterface::Get(); + if (!iface) + { + return AZ::Failure( + AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) + ); + } + return iface->StopHapticVibrationAction(actionHandle); + } + + }; // class OpenXRActions void OpenXRBehaviorReflect(AZ::BehaviorContext& context) { + IOpenXRActions::ActionHandle::Reflect(&context); + PoseWithVelocities::Reflect(&context); + context.Class("OpenXRVisualizedSpaces") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Module, "openxr") @@ -193,6 +387,24 @@ namespace OpenXRVk ->Method("GetViewPoses", &OpenXRVisualizedSpaces::GetViewPoses) ->Method("ForceViewPosesCacheUpdate", &OpenXRVisualizedSpaces::ForceViewPosesCacheUpdate) ; + + context.Class("OpenXRActions") + ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) + ->Attribute(AZ::Script::Attributes::Module, "openxr") + ->Method("GetAllActionSets", &OpenXRActions::GetAllActionSets) + ->Method("GetActionSetState", &OpenXRActions::GetActionSetState) + ->Method("SetActionSetState", &OpenXRActions::SetActionSetState) + ->Method("GetActionHandle", &OpenXRActions::GetActionHandle) + ->Method("GetActionStateBoolean", &OpenXRActions::GetActionStateBoolean) + ->Method("GetActionStateFloat", &OpenXRActions::GetActionStateFloat) + ->Method("GetActionStateVector2", &OpenXRActions::GetActionStateVector2) + ->Method("SetBaseVisualizedSpaceForPoseActions", &OpenXRActions::SetBaseVisualizedSpaceForPoseActions) + ->Method("GetBaseVisualizedSpaceForPoseActions", &OpenXRActions::GetBaseVisualizedSpaceForPoseActions) + ->Method("GetActionStatePose", &OpenXRActions::GetActionStatePose) + ->Method("GetActionStatePoseWithVelocities", &OpenXRActions::GetActionStatePoseWithVelocities) + ->Method("ApplyHapticVibrationAction", &OpenXRActions::ApplyHapticVibrationAction) + ->Method("StopHapticVibrationAction", &OpenXRActions::StopHapticVibrationAction) + ; } } diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp index 5b3c009cd..a124fd93e 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp @@ -174,35 +174,52 @@ namespace OpenXRVk // Button { m_movement.SetZ(0.0f); - auto actionHandle = actionsIFace->GetActionHandle("my_action_set", "button"); - if (!actionHandle.IsValid()) + do { - return; - } - auto outcome = actionsIFace->GetActionStateBoolean(actionHandle); - if (outcome.IsSuccess()) - { - if (outcome.GetValue()) + auto outcomeHandle = actionsIFace->GetActionHandle("my_action_set", "button"); + if (!outcomeHandle.IsSuccess()) + { + AZ_Error("XRCameraMovementComponent", false, "%s", outcomeHandle.GetError().c_str()); + break; + } + auto actionHandle = outcomeHandle.TakeValue(); + if (!actionHandle.IsValid()) + { + break; + } + auto outcomeState = actionsIFace->GetActionStateBoolean(actionHandle); + if (!outcomeState.IsSuccess()) + { + // The action is not active, which means the controler is not being used at the moment + break; + } + if (outcomeState.GetValue()) { - // up m_movement.SetZ(m_movementSensitivity); } - } - } - // Pose - { - auto actionHandle = actionsIFace->GetActionHandle("my_action_set", "left_pose"); - if (!actionHandle.IsValid()) - { - return; - } - auto outcome = actionsIFace->GetActionStatePose(actionHandle); - if (outcome.IsSuccess()) - { - AZ::Transform tm(outcome.TakeValue()); - //AZ_Printf("Galib", "left_pose tm=\n%s\n", AZStd::to_string(tm).c_str()); - } + } while (0); } + // // Pose + // { + // auto actionHandle = IOpenXRActions::ActionHandle(); + // { + // auto outcome = actionsIFace->GetActionHandle("my_action_set", "left_pose"); + // if (outcome.IsSuccess()) + // { + // actionHandle = outcome.TakeValue(); + // } + // } + // if (!actionHandle.IsValid()) + // { + // return; + // } + // auto outcome = actionsIFace->GetActionStatePose(actionHandle); + // if (outcome.IsSuccess()) + // { + // AZ::Transform tm(outcome.TakeValue()); + // //AZ_Printf("Galib", "left_pose tm=\n%s\n", AZStd::to_string(tm).c_str()); + // } + // } } From aee1bc2719954d17ac4740112f0f8139a0ab95ad Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sun, 28 Jan 2024 04:48:11 -0600 Subject: [PATCH 20/33] Haptics working from Lua Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h | 1 + Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h index aa91db92d..ef094db6d 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h @@ -73,6 +73,7 @@ namespace OpenXRVk //! Same as above, but also queries location (linear) and orientation (angular) velocities virtual AZ::Outcome GetActionStatePoseWithVelocities(ActionHandle actionHandle) const = 0; + //! @param amplitude Will be clamped between 0.0f and 1.0f. virtual AZ::Outcome ApplyHapticVibrationAction(ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) = 0; virtual AZ::Outcome StopHapticVibrationAction(ActionHandle actionHandle) = 0; }; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp index 4f1437feb..ac265cea2 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp @@ -30,7 +30,7 @@ namespace OpenXRVk { return XR_ACTION_TYPE_POSE_INPUT; } - else if (actionTypeStr == s_TypePoseStr) + else if (actionTypeStr == s_TypeVibrationStr) { return XR_ACTION_TYPE_VIBRATION_OUTPUT; } From ae485ef7764bee7fa3ccc0855d1a0a85ce6c7d35 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sun, 28 Jan 2024 05:13:25 -0600 Subject: [PATCH 21/33] VisualizedSpace to ReferenceSpace Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkActionsInterface.h | 10 +- ...e.h => OpenXRVkReferenceSpacesInterface.h} | 22 +-- .../Code/Include/OpenXRVk/OpenXRVkSession.h | 4 +- .../OpenXRVkActionSetsAssetBuilder.cpp | 6 +- .../OpenXRVkAssetBuildersSystemComponent.cpp | 6 +- .../OpenXRVkAssetBuildersSystemComponent.h | 2 +- .../Code/Source/OpenXRVkActionsManager.cpp | 32 ++--- .../Code/Source/OpenXRVkActionsManager.h | 8 +- .../Source/OpenXRVkBehaviorReflection.cpp | 134 +++++++++--------- Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp | 4 +- ...cpp => OpenXRVkReferenceSpacesManager.cpp} | 66 ++++----- ...ger.h => OpenXRVkReferenceSpacesManager.h} | 34 ++--- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 18 +-- .../Code/openxrvk_private_common_files.cmake | 6 +- 14 files changed, 176 insertions(+), 176 deletions(-) rename Gems/OpenXRVk/Code/Include/OpenXRVk/{OpenXRVkVisualizedSpacesInterface.h => OpenXRVkReferenceSpacesInterface.h} (82%) rename Gems/OpenXRVk/Code/Source/{OpenXRVkVisualizedSpacesManager.cpp => OpenXRVkReferenceSpacesManager.cpp} (77%) rename Gems/OpenXRVk/Code/Source/{OpenXRVkVisualizedSpacesManager.h => OpenXRVkReferenceSpacesManager.h} (69%) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h index ef094db6d..4a453bcaf 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h @@ -30,7 +30,7 @@ namespace OpenXRVk //! to drive the state of haptic feedback actions. //! The implementation is encouraged to expose each method //! of this interface as global functions in the behavior context. - //! REMARK: This class must be instantiated AFTER OpenXRVisualizedSpacesInterface. + //! REMARK: This class must be instantiated AFTER OpenXRReferenceSpacesInterface. class IOpenXRActions { public: @@ -62,10 +62,10 @@ namespace OpenXRVk //! Pose actions are also known as Action Spaces, and their Pose/Transform is always calculated //! using a reference/base visualized space. This is a stateful API that can be called at anytime. - //! See OpenXRVisualizedSpacesInterface to get details about Visualized Spaces. - //! By default the base VisualizedSpace is the "View" space, which represents thhe user's head centroid. - virtual AZ::Outcome SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) = 0; - virtual const AZStd::string& GetBaseVisualizedSpaceForPoseActions() const = 0; + //! See OpenXRReferenceSpacesInterface to get details about Visualized Spaces. + //! By default the base ReferenceSpace is the "View" space, which represents thhe user's head centroid. + virtual AZ::Outcome SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName) = 0; + virtual const AZStd::string& GetBaseReferenceSpaceForPoseActions() const = 0; //! The returned transform is relative to the base Visualized Space. virtual AZ::Outcome GetActionStatePose(ActionHandle actionHandle) const = 0; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkVisualizedSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h similarity index 82% rename from Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkVisualizedSpacesInterface.h rename to Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h index 302a905ee..7b32786ed 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkVisualizedSpacesInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h @@ -18,14 +18,14 @@ namespace OpenXRVk { //! REMARK: This class must be instantiated BEFORE OpenXRActionsInterface. - class IOpenXRVisualizedSpaces + class IOpenXRReferenceSpaces { public: - AZ_RTTI(IOpenXRVisualizedSpaces, "{244D24BE-DD6F-430A-8F99-1D24AC1665B6}"); - AZ_DISABLE_COPY_MOVE(IOpenXRVisualizedSpaces); + AZ_RTTI(IOpenXRReferenceSpaces, "{244D24BE-DD6F-430A-8F99-1D24AC1665B6}"); + AZ_DISABLE_COPY_MOVE(IOpenXRReferenceSpaces); - IOpenXRVisualizedSpaces() = default; - virtual ~IOpenXRVisualizedSpaces() = default; + IOpenXRReferenceSpaces() = default; + virtual ~IOpenXRReferenceSpaces() = default; // Identifiers for all reference spaces that all OpenXR implementations // must support. These are the default system spaces. Most OpenXR implementations @@ -41,16 +41,16 @@ namespace OpenXRVk static constexpr char ReferenceSpaceNameLocal[] = "Local"; static constexpr char ReferenceSpaceNameStage[] = "Stage"; - virtual AZStd::vector GetVisualizedSpaceNames() const = 0; - virtual AZ::Outcome AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) = 0; - virtual AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) = 0; + virtual AZStd::vector GetReferenceSpaceNames() const = 0; + virtual AZ::Outcome AddReferenceSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) = 0; + virtual AZ::Outcome RemoveReferenceSpace(const AZStd::string& spaceName) = 0; //! REMARK: Maybe it's a good idea to move this into a private interface for the OpenXRVk Gem, //! and privately use the native handle (XrSpace) - virtual const void * GetVisualizedSpaceNativeHandle(const AZStd::string& spaceName) const = 0; + virtual const void * GetReferenceSpaceNativeHandle(const AZStd::string& spaceName) const = 0; // Returns the Pose of @spaceName relative to @baseSpaceName. - virtual AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const = 0; + virtual AZ::Outcome GetReferenceSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const = 0; // By default the "View" space is the only space that will be automatically // located on each frame. The "View" space represents the User's head centroid. @@ -92,6 +92,6 @@ namespace OpenXRVk virtual void ForceViewPosesCacheUpdate() = 0; }; - using OpenXRVisualizedSpacesInterface = AZ::Interface; + using OpenXRReferenceSpacesInterface = AZ::Interface; } // namespace OpenXRVk \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h index 5b260ce19..3ab078572 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h @@ -16,7 +16,7 @@ namespace OpenXRVk { class ActionsManager; - class VisualizedSpacesManager; + class ReferenceSpacesManager; // Class that will help manage XrSession class Session final @@ -98,7 +98,7 @@ namespace OpenXRVk XrInstance m_xrInstance = XR_NULL_HANDLE; XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR }; - AZStd::unique_ptr m_visualizedSpacesMgr; + AZStd::unique_ptr m_referenceSpacesMgr; AZStd::unique_ptr m_actionsMgr; // Application defined base space that will used to calculate diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp index de97c8763..ffb3a7085 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp @@ -9,10 +9,10 @@ #include #include -#include -#include +#include +#include -#include "OpenXRActionSetsAssetBuilder.h" +#include "OpenXRVkActionSetsAssetBuilder.h" #pragma optimize( "", off ) // GALIB diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp index a27cd3203..19d541b62 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp @@ -9,10 +9,10 @@ #include #include -#include -#include +#include +#include -#include "OpenXRAssetBuildersSystemComponent.h" +#include "OpenXRVkAssetBuildersSystemComponent.h" namespace OpenXRVkBuilders { diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h index 28bc3086c..02b55fcd6 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h @@ -10,7 +10,7 @@ #include -#include "OpenXRActionSetsAssetBuilder.h" +#include "OpenXRVkActionSetsAssetBuilder.h" namespace OpenXRVkBuilders { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp index 9a6b9ee6a..074c73b2e 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp @@ -14,7 +14,7 @@ //#include #include -#include +#include #include "OpenXRVkActionsManager.h" namespace OpenXRVk @@ -115,12 +115,12 @@ namespace OpenXRVk m_xrInstance = xrInstance; m_xrSession = xrSession; - auto outcome = SetBaseVisualizedSpaceForPoseActions(IOpenXRVisualizedSpaces::ReferenceSpaceNameView); + auto outcome = SetBaseReferenceSpaceForPoseActions(IOpenXRReferenceSpaces::ReferenceSpaceNameView); if (!outcome.IsSuccess()) { const auto outcomeMsg = outcome.TakeError(); auto errorMsg = AZStd::string::format("Failed to set [%s] as the default base visualized space. Reason:\n%s.", - IOpenXRVisualizedSpaces::ReferenceSpaceNameView, outcomeMsg.c_str()); + IOpenXRReferenceSpaces::ReferenceSpaceNameView, outcomeMsg.c_str()); AZ_Assert(false, "%s", errorMsg.c_str()); AZ_Error(LogName, false, "%s", errorMsg.c_str()); return false; @@ -618,36 +618,36 @@ namespace OpenXRVk return AZ::Success(AZ::Vector2(state.currentState.x, state.currentState.y)); } - AZ::Outcome ActionsManager::SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) + AZ::Outcome ActionsManager::SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName) { - if (visualizedSpaceName == m_baseVisualizedSpaceName) + if (visualizedSpaceName == m_baseReferenceSpaceName) { return AZ::Success(true); } - auto visualizedSpacesIface = OpenXRVisualizedSpacesInterface::Get(); + auto visualizedSpacesIface = OpenXRReferenceSpacesInterface::Get(); if (!visualizedSpacesIface) { - AZ_Assert(false, "The OpenXRVisualizedSpacesInterface doesn't exist!"); - return AZ::Failure("The OpenXRVisualizedSpacesInterface doesn't exist!"); + AZ_Assert(false, "The OpenXRReferenceSpacesInterface doesn't exist!"); + return AZ::Failure("The OpenXRReferenceSpacesInterface doesn't exist!"); } - const void* opaqueXrSpace = visualizedSpacesIface->GetVisualizedSpaceNativeHandle(visualizedSpaceName); + const void* opaqueXrSpace = visualizedSpacesIface->GetReferenceSpaceNativeHandle(visualizedSpaceName); if (!opaqueXrSpace) { return AZ::Failure( AZStd::string::format("Visualized space with name [%s] doesn't exist. Will keep the current base space named [%s]", - visualizedSpaceName.c_str(), m_baseVisualizedSpaceName.c_str()) + visualizedSpaceName.c_str(), m_baseReferenceSpaceName.c_str()) ); } - m_baseVisualizedSpaceName = visualizedSpaceName; - m_xrBaseVisualizedSpace = reinterpret_cast(const_cast(opaqueXrSpace)); + m_baseReferenceSpaceName = visualizedSpaceName; + m_xrBaseReferenceSpace = reinterpret_cast(const_cast(opaqueXrSpace)); return AZ::Success(true); } - const AZStd::string& ActionsManager::GetBaseVisualizedSpaceForPoseActions() const + const AZStd::string& ActionsManager::GetBaseReferenceSpaceForPoseActions() const { - return m_baseVisualizedSpaceName; + return m_baseReferenceSpaceName; } AZ::Outcome ActionsManager::GetActionStatePose(ActionHandle actionHandle) const @@ -675,7 +675,7 @@ namespace OpenXRVk } XrSpaceLocation spaceLocation {XR_TYPE_SPACE_LOCATION}; - result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseVisualizedSpace, m_predictedDisplaytime, &spaceLocation); + result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseReferenceSpace, m_predictedDisplaytime, &spaceLocation); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); @@ -707,7 +707,7 @@ namespace OpenXRVk XrSpaceVelocity spaceVelocity{ XR_TYPE_SPACE_VELOCITY }; XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION, &spaceVelocity}; - XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseVisualizedSpace, m_predictedDisplaytime, &spaceLocation); + XrResult result = xrLocateSpace(m_actions[actionIndex].m_xrSpace, m_xrBaseReferenceSpace, m_predictedDisplaytime, &spaceLocation); if (IsError(result)) { return AZ::Failure(AZStd::string(GetResultString(result))); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h index d9cebeedd..42c24cfdd 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h @@ -58,8 +58,8 @@ namespace OpenXRVk AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const override; AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) const override; - AZ::Outcome SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) override; - const AZStd::string& GetBaseVisualizedSpaceForPoseActions() const override; + AZ::Outcome SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName) override; + const AZStd::string& GetBaseReferenceSpaceForPoseActions() const override; AZ::Outcome GetActionStatePose(ActionHandle actionHandle) const override; AZ::Outcome GetActionStatePoseWithVelocities(ActionHandle actionHandle) const override; @@ -160,8 +160,8 @@ namespace OpenXRVk // Updated each time SyncActions is called. XrTime m_predictedDisplaytime; - AZStd::string m_baseVisualizedSpaceName; - XrSpace m_xrBaseVisualizedSpace = XR_NULL_HANDLE; + AZStd::string m_baseReferenceSpaceName; + XrSpace m_xrBaseReferenceSpace = XR_NULL_HANDLE; //! Each actionSet in this list is guaranteed to contain at least one valid action. AZStd::vector m_actionSets; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp index ad52d8783..808c11cb1 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp @@ -6,7 +6,7 @@ * */ -#include +#include #include #include "OpenXRVkBehaviorReflection.h" @@ -37,72 +37,72 @@ namespace OpenXRVk } } - class OpenXRVisualizedSpaces + class OpenXRReferenceSpaces { public: - AZ_TYPE_INFO(OpenXRVisualizedSpaces, "{A060D5C5-1514-421B-9AAA-1E276BA2E33E}"); - AZ_CLASS_ALLOCATOR(OpenXRVisualizedSpaces, AZ::SystemAllocator); + AZ_TYPE_INFO(OpenXRReferenceSpaces, "{A060D5C5-1514-421B-9AAA-1E276BA2E33E}"); + AZ_CLASS_ALLOCATOR(OpenXRReferenceSpaces, AZ::SystemAllocator); - static constexpr char LogName[] = "OpenXRVisualizedSpaces"; + static constexpr char LogName[] = "OpenXRReferenceSpaces"; - OpenXRVisualizedSpaces() = default; - ~OpenXRVisualizedSpaces() = default; + OpenXRReferenceSpaces() = default; + ~OpenXRReferenceSpaces() = default; - static AZStd::vector GetVisualizedSpaceNames() + static AZStd::vector GetReferenceSpaceNames() { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); return {}; } - return iface->GetVisualizedSpaceNames(); + return iface->GetReferenceSpaceNames(); } - static AZ::Outcome AddVisualizedSpace(IOpenXRVisualizedSpaces::ReferenceSpaceId referenceSpaceType, + static AZ::Outcome AddReferenceSpace(IOpenXRReferenceSpaces::ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { return AZ::Failure( - AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + AZStd::string::format("%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__) ); } - return iface->AddVisualizedSpace(referenceSpaceType, spaceName, poseInReferenceSpace); + return iface->AddReferenceSpace(referenceSpaceType, spaceName, poseInReferenceSpace); } - static AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) + static AZ::Outcome RemoveReferenceSpace(const AZStd::string& spaceName) { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { return AZ::Failure( - AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + AZStd::string::format("%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__) ); } - return iface->RemoveVisualizedSpace(spaceName); + return iface->RemoveReferenceSpace(spaceName); } - static AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) + static AZ::Outcome GetReferenceSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { return AZ::Failure( - AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + AZStd::string::format("%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__) ); } - return iface->GetVisualizedSpacePose(spaceName, baseSpaceName); + return iface->GetReferenceSpacePose(spaceName, baseSpaceName); } static AZ::Outcome SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { return AZ::Failure( - AZStd::string::format("%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__) + AZStd::string::format("%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__) ); } return iface->SetBaseSpaceForViewSpacePose(spaceName); @@ -110,10 +110,10 @@ namespace OpenXRVk static const AZStd::string& GetBaseSpaceForViewSpacePose() { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); static const AZStd::string emptyStr; return emptyStr; } @@ -122,10 +122,10 @@ namespace OpenXRVk static const AZ::Transform& GetViewSpacePose() { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); return AZ::Transform::Identity(); } return iface->GetViewSpacePose(); @@ -133,10 +133,10 @@ namespace OpenXRVk static uint32_t GetViewCount() { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); return 0; } return iface->GetViewCount(); @@ -144,10 +144,10 @@ namespace OpenXRVk static const AZ::Transform& GetViewPose(uint32_t eyeIndex) { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); return AZ::Transform::Identity(); } return iface->GetViewPose(eyeIndex); @@ -156,10 +156,10 @@ namespace OpenXRVk //TODO: Serialize AZ::RPI::FovData static const AZ::RPI::FovData& GetViewFovData(uint32_t eyeIndex) { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); static const AZ::RPI::FovData fovData{}; return fovData; } @@ -168,10 +168,10 @@ namespace OpenXRVk static const AZStd::vector& GetViewPoses() { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); static AZStd::vector EmptyList; return EmptyList; } @@ -180,16 +180,16 @@ namespace OpenXRVk static void ForceViewPosesCacheUpdate() { - const auto iface = OpenXRVisualizedSpacesInterface::Get(); + const auto iface = OpenXRReferenceSpacesInterface::Get(); if (!iface) { - AZ_Error(LogName, false, "%s: OpenXRVisualizedSpacesInterface is not available.", __FUNCTION__); + AZ_Error(LogName, false, "%s: OpenXRReferenceSpacesInterface is not available.", __FUNCTION__); return; } iface->ForceViewPosesCacheUpdate(); } - }; // class OpenXRVisualizedSpaces + }; // class OpenXRReferenceSpaces class OpenXRActions { @@ -283,7 +283,7 @@ namespace OpenXRVk return iface->GetActionStateVector2(actionHandle); } - static AZ::Outcome SetBaseVisualizedSpaceForPoseActions(const AZStd::string& visualizedSpaceName) + static AZ::Outcome SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName) { const auto iface = OpenXRActionsInterface::Get(); if (!iface) @@ -292,10 +292,10 @@ namespace OpenXRVk AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) ); } - return iface->SetBaseVisualizedSpaceForPoseActions(visualizedSpaceName); + return iface->SetBaseReferenceSpaceForPoseActions(visualizedSpaceName); } - static const AZStd::string& GetBaseVisualizedSpaceForPoseActions() + static const AZStd::string& GetBaseReferenceSpaceForPoseActions() { const auto iface = OpenXRActionsInterface::Get(); if (!iface) @@ -304,7 +304,7 @@ namespace OpenXRVk static const AZStd::string emptyStr; return emptyStr; } - return iface->GetBaseVisualizedSpaceForPoseActions(); + return iface->GetBaseReferenceSpaceForPoseActions(); } static AZ::Outcome GetActionStatePose(IOpenXRActions::ActionHandle actionHandle) @@ -362,30 +362,30 @@ namespace OpenXRVk IOpenXRActions::ActionHandle::Reflect(&context); PoseWithVelocities::Reflect(&context); - context.Class("OpenXRVisualizedSpaces") + context.Class("OpenXRReferenceSpaces") ->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Common) ->Attribute(AZ::Script::Attributes::Module, "openxr") - ->Constant("ReferenceSpaceIdView", BehaviorConstant(IOpenXRVisualizedSpaces::ReferenceSpaceIdView)) - ->Constant("ReferenceSpaceIdLocal", BehaviorConstant(IOpenXRVisualizedSpaces::ReferenceSpaceIdLocal)) - ->Constant("ReferenceSpaceIdStage", BehaviorConstant(IOpenXRVisualizedSpaces::ReferenceSpaceIdStage)) - ->Constant("ReferenceSpaceNameView", BehaviorConstant(AZStd::string(IOpenXRVisualizedSpaces::ReferenceSpaceNameView))) - ->Constant("ReferenceSpaceNameLocal", BehaviorConstant(AZStd::string(IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal))) - ->Constant("ReferenceSpaceNameStage", BehaviorConstant(AZStd::string(IOpenXRVisualizedSpaces::ReferenceSpaceNameStage))) - ->Constant("LeftEyeViewId", BehaviorConstant(IOpenXRVisualizedSpaces::LeftEyeView)) - ->Constant("RightEyeViewId", BehaviorConstant(IOpenXRVisualizedSpaces::RightEyeView)) - ->Method("GetVisualizedSpaceNames", &OpenXRVisualizedSpaces::GetVisualizedSpaceNames) - ->Method("AddVisualizedSpace", &OpenXRVisualizedSpaces::AddVisualizedSpace) - ->Method("RemoveVisualizedSpace", &OpenXRVisualizedSpaces::RemoveVisualizedSpace) - ->Method("GetVisualizedSpacePose", &OpenXRVisualizedSpaces::GetVisualizedSpacePose) - ->Method("SetBaseSpaceForViewSpacePose", &OpenXRVisualizedSpaces::SetBaseSpaceForViewSpacePose) - ->Method("GetBaseSpaceForViewSpacePose", &OpenXRVisualizedSpaces::GetBaseSpaceForViewSpacePose) - ->Method("GetViewSpacePose", &OpenXRVisualizedSpaces::GetViewSpacePose) - ->Method("GetViewCount", &OpenXRVisualizedSpaces::GetViewCount) - ->Method("GetViewPose", &OpenXRVisualizedSpaces::GetViewPose) + ->Constant("ReferenceSpaceIdView", BehaviorConstant(IOpenXRReferenceSpaces::ReferenceSpaceIdView)) + ->Constant("ReferenceSpaceIdLocal", BehaviorConstant(IOpenXRReferenceSpaces::ReferenceSpaceIdLocal)) + ->Constant("ReferenceSpaceIdStage", BehaviorConstant(IOpenXRReferenceSpaces::ReferenceSpaceIdStage)) + ->Constant("ReferenceSpaceNameView", BehaviorConstant(AZStd::string(IOpenXRReferenceSpaces::ReferenceSpaceNameView))) + ->Constant("ReferenceSpaceNameLocal", BehaviorConstant(AZStd::string(IOpenXRReferenceSpaces::ReferenceSpaceNameLocal))) + ->Constant("ReferenceSpaceNameStage", BehaviorConstant(AZStd::string(IOpenXRReferenceSpaces::ReferenceSpaceNameStage))) + ->Constant("LeftEyeViewId", BehaviorConstant(IOpenXRReferenceSpaces::LeftEyeView)) + ->Constant("RightEyeViewId", BehaviorConstant(IOpenXRReferenceSpaces::RightEyeView)) + ->Method("GetReferenceSpaceNames", &OpenXRReferenceSpaces::GetReferenceSpaceNames) + ->Method("AddReferenceSpace", &OpenXRReferenceSpaces::AddReferenceSpace) + ->Method("RemoveReferenceSpace", &OpenXRReferenceSpaces::RemoveReferenceSpace) + ->Method("GetReferenceSpacePose", &OpenXRReferenceSpaces::GetReferenceSpacePose) + ->Method("SetBaseSpaceForViewSpacePose", &OpenXRReferenceSpaces::SetBaseSpaceForViewSpacePose) + ->Method("GetBaseSpaceForViewSpacePose", &OpenXRReferenceSpaces::GetBaseSpaceForViewSpacePose) + ->Method("GetViewSpacePose", &OpenXRReferenceSpaces::GetViewSpacePose) + ->Method("GetViewCount", &OpenXRReferenceSpaces::GetViewCount) + ->Method("GetViewPose", &OpenXRReferenceSpaces::GetViewPose) //TODO: Serialize AZ::RPI::FovData - //->Method("GetViewFovData", &OpenXRVisualizedSpaces::GetViewFovData) - ->Method("GetViewPoses", &OpenXRVisualizedSpaces::GetViewPoses) - ->Method("ForceViewPosesCacheUpdate", &OpenXRVisualizedSpaces::ForceViewPosesCacheUpdate) + //->Method("GetViewFovData", &OpenXRReferenceSpaces::GetViewFovData) + ->Method("GetViewPoses", &OpenXRReferenceSpaces::GetViewPoses) + ->Method("ForceViewPosesCacheUpdate", &OpenXRReferenceSpaces::ForceViewPosesCacheUpdate) ; context.Class("OpenXRActions") @@ -398,8 +398,8 @@ namespace OpenXRVk ->Method("GetActionStateBoolean", &OpenXRActions::GetActionStateBoolean) ->Method("GetActionStateFloat", &OpenXRActions::GetActionStateFloat) ->Method("GetActionStateVector2", &OpenXRActions::GetActionStateVector2) - ->Method("SetBaseVisualizedSpaceForPoseActions", &OpenXRActions::SetBaseVisualizedSpaceForPoseActions) - ->Method("GetBaseVisualizedSpaceForPoseActions", &OpenXRActions::GetBaseVisualizedSpaceForPoseActions) + ->Method("SetBaseReferenceSpaceForPoseActions", &OpenXRActions::SetBaseReferenceSpaceForPoseActions) + ->Method("GetBaseReferenceSpaceForPoseActions", &OpenXRActions::GetBaseReferenceSpaceForPoseActions) ->Method("GetActionStatePose", &OpenXRActions::GetActionStatePose) ->Method("GetActionStatePoseWithVelocities", &OpenXRActions::GetActionStatePoseWithVelocities) ->Method("ApplyHapticVibrationAction", &OpenXRActions::ApplyHapticVibrationAction) diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp index 548271061..22c7eb549 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -166,7 +166,7 @@ namespace OpenXRVk AZ::RHI::ResultCode Device::GetViewFov([[maybe_unused]] AZ::u32 viewIndex, [[maybe_unused]] AZ::RPI::FovData& outFovData) const { - if (auto spacesIface = OpenXRVisualizedSpacesInterface::Get(); + if (auto spacesIface = OpenXRReferenceSpacesInterface::Get(); spacesIface != nullptr) { outFovData = spacesIface->GetViewFovData(viewIndex); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp similarity index 77% rename from Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.cpp rename to Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp index a8f8b3ded..3439fe37b 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp @@ -11,11 +11,11 @@ #include -#include "OpenXRVkVisualizedSpacesManager.h" +#include "OpenXRVkReferenceSpacesManager.h" namespace OpenXRVk { - bool VisualizedSpacesManager::Init(XrInstance xrInstance, XrSession xrSession, XrViewConfigurationType xrViewConfigurationType, uint32_t numEyeViews) + bool ReferenceSpacesManager::Init(XrInstance xrInstance, XrSession xrSession, XrViewConfigurationType xrViewConfigurationType, uint32_t numEyeViews) { m_xrInstance = xrInstance; m_xrSession = xrSession; @@ -33,14 +33,14 @@ namespace OpenXRVk const auto identityTm = AZ::Transform::CreateIdentity(); AZStd::array, 3> ReferenceSpaces = { { - {IOpenXRVisualizedSpaces::ReferenceSpaceIdView, IOpenXRVisualizedSpaces::ReferenceSpaceNameView }, - {IOpenXRVisualizedSpaces::ReferenceSpaceIdLocal, IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal }, - {IOpenXRVisualizedSpaces::ReferenceSpaceIdStage, IOpenXRVisualizedSpaces::ReferenceSpaceNameStage } + {IOpenXRReferenceSpaces::ReferenceSpaceIdView, IOpenXRReferenceSpaces::ReferenceSpaceNameView }, + {IOpenXRReferenceSpaces::ReferenceSpaceIdLocal, IOpenXRReferenceSpaces::ReferenceSpaceNameLocal }, + {IOpenXRReferenceSpaces::ReferenceSpaceIdStage, IOpenXRReferenceSpaces::ReferenceSpaceNameStage } } }; for (const auto& refPair : ReferenceSpaces) { - if (auto outcome = AddVisualizedSpace(refPair.first, { refPair.second }, identityTm); + if (auto outcome = AddReferenceSpace(refPair.first, { refPair.second }, identityTm); !outcome.IsSuccess()) { AZ_Error(LogName, false, "Failed to create default reference space [%s].Reason:\n%s\n", @@ -50,15 +50,15 @@ namespace OpenXRVk } // Set the default base space for View Space pose calculation. - auto outcome = SetBaseSpaceForViewSpacePose({ IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal }); + auto outcome = SetBaseSpaceForViewSpacePose({ IOpenXRReferenceSpaces::ReferenceSpaceNameLocal }); AZ_Assert(outcome.IsSuccess(), "Failed to set the base space for View Space pose location"); - const auto& viewSpace = m_spaces.at(IOpenXRVisualizedSpaces::ReferenceSpaceNameView); + const auto& viewSpace = m_spaces.at(IOpenXRReferenceSpaces::ReferenceSpaceNameView); m_viewSpace = &viewSpace; return true; } - bool VisualizedSpacesManager::SyncViews(XrTime predictedDisplayTime) + bool ReferenceSpacesManager::SyncViews(XrTime predictedDisplayTime) { m_predictedDisplaytime = predictedDisplayTime; @@ -75,24 +75,24 @@ namespace OpenXRVk return true; } - void VisualizedSpacesManager::ResetSpaces() + void ReferenceSpacesManager::ResetSpaces() { AZ_Error(LogName, false, "FIXME! %s", __FUNCTION__); } - const AZStd::vector& VisualizedSpacesManager::GetXrViews() const + const AZStd::vector& ReferenceSpacesManager::GetXrViews() const { return m_xrViews; } - XrSpace VisualizedSpacesManager::GetViewSpaceXrSpace() const + XrSpace ReferenceSpacesManager::GetViewSpaceXrSpace() const { return m_viewSpace->m_xrSpace; } ///////////////////////////////////////////////// - /// OpenXRVisualizedSpacesInterface overrides - AZStd::vector VisualizedSpacesManager::GetVisualizedSpaceNames() const + /// OpenXRReferenceSpacesInterface overrides + AZStd::vector ReferenceSpacesManager::GetReferenceSpaceNames() const { AZStd::vector retList; for (auto const& pair : m_spaces) { @@ -101,7 +101,7 @@ namespace OpenXRVk return retList; } - AZ::Outcome VisualizedSpacesManager::AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, + AZ::Outcome ReferenceSpacesManager::AddReferenceSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) { if (m_spaces.contains(spaceName)) @@ -119,18 +119,18 @@ namespace OpenXRVk ); } - VisualizedSpace visualizedSpace{ spaceName, poseInReferenceSpace, newXrSpace }; + ReferenceSpace visualizedSpace{ spaceName, poseInReferenceSpace, newXrSpace }; m_spaces.emplace(spaceName, AZStd::move(visualizedSpace)); return AZ::Success(true); } - AZ::Outcome VisualizedSpacesManager::RemoveVisualizedSpace(const AZStd::string& spaceName) + AZ::Outcome ReferenceSpacesManager::RemoveReferenceSpace(const AZStd::string& spaceName) { static const AZStd::unordered_set defaultSystemSpaces { - {IOpenXRVisualizedSpaces::ReferenceSpaceNameView }, - {IOpenXRVisualizedSpaces::ReferenceSpaceNameLocal}, - {IOpenXRVisualizedSpaces::ReferenceSpaceNameStage} + {IOpenXRReferenceSpaces::ReferenceSpaceNameView }, + {IOpenXRReferenceSpaces::ReferenceSpaceNameLocal}, + {IOpenXRReferenceSpaces::ReferenceSpaceNameStage} }; if (defaultSystemSpaces.contains(spaceName)) { @@ -144,7 +144,7 @@ namespace OpenXRVk (m_baseSpaceForViewSpace->m_name == spaceName)) { return AZ::Failure(AZStd::string::format("Can not remove space [%s] because it is the base space to locate the [%s] space pose.", - spaceName.c_str(), IOpenXRVisualizedSpaces::ReferenceSpaceNameView)); + spaceName.c_str(), IOpenXRReferenceSpaces::ReferenceSpaceNameView)); } auto itor = m_spaces.find(spaceName); @@ -159,7 +159,7 @@ namespace OpenXRVk return AZ::Success(true); } - const void * VisualizedSpacesManager::GetVisualizedSpaceNativeHandle(const AZStd::string& spaceName) const + const void * ReferenceSpacesManager::GetReferenceSpaceNativeHandle(const AZStd::string& spaceName) const { const auto spaceItor = m_spaces.find(spaceName); if (spaceItor == m_spaces.end()) @@ -170,7 +170,7 @@ namespace OpenXRVk } - AZ::Outcome VisualizedSpacesManager::GetVisualizedSpacePose(const AZStd::string& spaceName, + AZ::Outcome ReferenceSpacesManager::GetReferenceSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const { const auto spaceItor = m_spaces.find(spaceName); @@ -200,7 +200,7 @@ namespace OpenXRVk return AZ::Success(AzTransformFromXrPose(xrSpaceLocation.pose)); } - AZ::Outcome VisualizedSpacesManager::SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) + AZ::Outcome ReferenceSpacesManager::SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) { const auto baseSpaceItor = m_spaces.find(spaceName); if (baseSpaceItor == m_spaces.end()) @@ -215,23 +215,23 @@ namespace OpenXRVk return AZ::Success(true); } - const AZStd::string& VisualizedSpacesManager::GetBaseSpaceForViewSpacePose() const + const AZStd::string& ReferenceSpacesManager::GetBaseSpaceForViewSpacePose() const { AZ_Assert(m_baseSpaceForViewSpace != nullptr, "A base space is always expected to exist!"); return m_baseSpaceForViewSpace->m_name; } - const AZ::Transform& VisualizedSpacesManager::GetViewSpacePose() const + const AZ::Transform& ReferenceSpacesManager::GetViewSpacePose() const { return m_viewSpacePose; } - uint32_t VisualizedSpacesManager::GetViewCount() const + uint32_t ReferenceSpacesManager::GetViewCount() const { return aznumeric_cast(m_eyeViewPoses.size()); } - const AZ::Transform& VisualizedSpacesManager::GetViewPose(uint32_t eyeIndex) const + const AZ::Transform& ReferenceSpacesManager::GetViewPose(uint32_t eyeIndex) const { if (eyeIndex >= m_eyeViewPoses.size()) { @@ -245,7 +245,7 @@ namespace OpenXRVk return m_eyeViewPoses[eyeIndex]; } - const AZ::RPI::FovData& VisualizedSpacesManager::GetViewFovData(uint32_t eyeIndex) const + const AZ::RPI::FovData& ReferenceSpacesManager::GetViewFovData(uint32_t eyeIndex) const { if (eyeIndex >= m_eyeViewPoses.size()) { @@ -260,12 +260,12 @@ namespace OpenXRVk return m_eyeViewFovDatas[eyeIndex]; } - const AZStd::vector& VisualizedSpacesManager::GetViewPoses() const + const AZStd::vector& ReferenceSpacesManager::GetViewPoses() const { return m_eyeViewPoses; } - void VisualizedSpacesManager::ForceViewPosesCacheUpdate() + void ReferenceSpacesManager::ForceViewPosesCacheUpdate() { XrViewState viewState{ XR_TYPE_VIEW_STATE }; uint32_t viewCapacityInput = aznumeric_cast(m_xrViews.size()); @@ -298,10 +298,10 @@ namespace OpenXRVk fovData.m_angleDown = xrFov.angleDown; } } - /// OpenXRVisualizedSpacesInterface overrides + /// OpenXRReferenceSpacesInterface overrides ///////////////////////////////////////////////// - XrSpace VisualizedSpacesManager::CreateXrSpace(XrReferenceSpaceType referenceSpaceType, const AZ::Transform& relativePose) + XrSpace ReferenceSpacesManager::CreateXrSpace(XrReferenceSpaceType referenceSpaceType, const AZ::Transform& relativePose) { XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{ XR_TYPE_REFERENCE_SPACE_CREATE_INFO }; referenceSpaceCreateInfo.poseInReferenceSpace = XrPoseFromAzTransform(relativePose); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h similarity index 69% rename from Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.h rename to Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h index 625f1f388..a4592c2ad 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkVisualizedSpacesManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h @@ -10,18 +10,18 @@ #include -#include +#include namespace OpenXRVk { - class VisualizedSpacesManager final : - public OpenXRVisualizedSpacesInterface::Registrar + class ReferenceSpacesManager final : + public OpenXRReferenceSpacesInterface::Registrar { public: - AZ_CLASS_ALLOCATOR(VisualizedSpacesManager, AZ::SystemAllocator); - AZ_RTTI(VisualizedSpacesManager, "{4BC4D0C0-02D4-4676-8352-8BC51306AF02}", IOpenXRVisualizedSpaces) + AZ_CLASS_ALLOCATOR(ReferenceSpacesManager, AZ::SystemAllocator); + AZ_RTTI(ReferenceSpacesManager, "{4BC4D0C0-02D4-4676-8352-8BC51306AF02}", IOpenXRReferenceSpaces) - static constexpr char LogName[] = "OpenXRVkVisualizedSpacesManager"; + static constexpr char LogName[] = "OpenXRVkReferenceSpacesManager"; //! Initialize various actions and actionSets according to the //! "openxr.xractions" action bindings asset. @@ -39,15 +39,15 @@ namespace OpenXRVk XrSpace GetViewSpaceXrSpace() const; ///////////////////////////////////////////////// - /// OpenXRVisualizedSpacesInterface overrides - AZStd::vector GetVisualizedSpaceNames() const override; - AZ::Outcome AddVisualizedSpace(ReferenceSpaceId referenceSpaceType, + /// OpenXRReferenceSpacesInterface overrides + AZStd::vector GetReferenceSpaceNames() const override; + AZ::Outcome AddReferenceSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) override; - AZ::Outcome RemoveVisualizedSpace(const AZStd::string& spaceName) override; + AZ::Outcome RemoveReferenceSpace(const AZStd::string& spaceName) override; - const void * GetVisualizedSpaceNativeHandle(const AZStd::string& spaceName) const override; + const void * GetReferenceSpaceNativeHandle(const AZStd::string& spaceName) const override; - AZ::Outcome GetVisualizedSpacePose(const AZStd::string& spaceName, + AZ::Outcome GetReferenceSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const override; AZ::Outcome SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) override; @@ -60,7 +60,7 @@ namespace OpenXRVk const AZStd::vector& GetViewPoses() const override; void ForceViewPosesCacheUpdate() override; - /// OpenXRVisualizedSpacesInterface overrides + /// OpenXRReferenceSpacesInterface overrides ///////////////////////////////////////////////// private: @@ -72,7 +72,7 @@ namespace OpenXRVk // Updated each time the Session calls UpdateViewSpacePoseAndEyeViewPoses(). XrTime m_predictedDisplaytime; - struct VisualizedSpace + struct ReferenceSpace { AZStd::string m_name; //! We shave this transform in case we have to reset the spaces. @@ -83,11 +83,11 @@ namespace OpenXRVk // At the bare minimum this map will always contain three spaces that can not be removed: // "View", "Local" and "Stage". - AZStd::unordered_map m_spaces; + AZStd::unordered_map m_spaces; // We cache here the base space what will be used to "locate" the View Space pose. - const VisualizedSpace* m_baseSpaceForViewSpace; - const VisualizedSpace* m_viewSpace; // Cached for convenience. This pointer is set once during initialization and never changes. + const ReferenceSpace* m_baseSpaceForViewSpace; + const ReferenceSpace* m_viewSpace; // Cached for convenience. This pointer is set once during initialization and never changes. AZ::Transform m_viewSpacePose; //! The following poses are always relative to @m_viewSpacePose. diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index 79c97b652..481612bfb 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -21,7 +21,7 @@ #include #include -#include "OpenXRVkVisualizedSpacesManager.h" +#include "OpenXRVkReferenceSpacesManager.h" #include "OpenXRVkActionsManager.h" @@ -55,8 +55,8 @@ namespace OpenXRVk XrResult result = xrCreateSession(m_xrInstance, &createInfo, &m_session); ASSERT_IF_UNSUCCESSFUL(result); - m_visualizedSpacesMgr = AZStd::make_unique(); - bool success = m_visualizedSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); + m_referenceSpacesMgr = AZStd::make_unique(); + bool success = m_referenceSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); m_actionsMgr = AZStd::make_unique(); @@ -128,7 +128,7 @@ namespace OpenXRVk // is not wearing the headset. Each time the proximity sensor is disabled or the user // decides to wear the headset, the XrSpaces need to be recreated, otherwise their // poses would be corrupted. - m_visualizedSpacesMgr->ResetSpaces(); + m_referenceSpacesMgr->ResetSpaces(); break; } case XR_SESSION_STATE_STOPPING: @@ -402,12 +402,12 @@ namespace OpenXRVk const AZStd::vector& Session::GetXrViews() const { - return m_visualizedSpacesMgr->GetXrViews(); + return m_referenceSpacesMgr->GetXrViews(); } XrSpace Session::GetViewSpaceXrSpace() const { - return m_visualizedSpacesMgr->GetViewSpaceXrSpace(); + return m_referenceSpacesMgr->GetViewSpaceXrSpace(); } bool Session::IsSessionRunning() const @@ -451,11 +451,11 @@ namespace OpenXRVk m_actionsMgr->SyncActions(predictedDisplayTime); } - m_visualizedSpacesMgr->SyncViews(predictedDisplayTime); + m_referenceSpacesMgr->SyncViews(predictedDisplayTime); //Notify the rest of the engine. AZ::RPI::XRSpaceNotificationBus::Broadcast(&AZ::RPI::XRSpaceNotifications::OnXRSpaceLocationsChanged, - m_visualizedSpacesMgr->GetViewSpacePose(), - m_visualizedSpacesMgr->GetViewPoses()); + m_referenceSpacesMgr->GetViewSpacePose(), + m_referenceSpacesMgr->GetViewPoses()); } } diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index c728cfc71..ffaa70e15 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -18,7 +18,7 @@ set(FILES Include/OpenXRVk/OpenXRVkSystemComponent.h Include/OpenXRVk/OpenXRVkUtils.h Include/OpenXRVk/OpenXRVkActionsInterface.h - Include/OpenXRVk/OpenXRVkVisualizedSpacesInterface.h + Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h Include/OpenXRVk/OpenXRVkActionSetsAsset.h Source/InputDeviceXRController.cpp @@ -36,8 +36,8 @@ set(FILES Source/OpenXRVkActionSetsAsset.cpp Source/XRCameraMovementComponent.cpp Source/XRCameraMovementComponent.h - Source/OpenXRVkVisualizedSpacesManager.cpp - Source/OpenXRVkVisualizedSpacesManager.h + Source/OpenXRVkReferenceSpacesManager.cpp + Source/OpenXRVkReferenceSpacesManager.h Source/OpenXRVkActionsManager.cpp Source/OpenXRVkActionsManager.h Source/OpenXRVkBehaviorReflection.cpp From 1fe03822ec1bd427f21f4f60ec1fdd4189bb0da6 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:54:02 -0600 Subject: [PATCH 22/33] almost done with ActionSets Asset validation Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVkInteractionProfilesAsset.h | 1 + .../OpenXRVkActionSetsAssetBuilder.cpp | 326 +++++++++++++++++- .../Source/Builders/OpenXRVkBuilderModule.cpp | 2 +- .../OpenXRVkInteractionProfilesAsset.cpp | 21 +- 4 files changed, 333 insertions(+), 17 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h index 5155aeafe..4b3e354b8 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h @@ -86,6 +86,7 @@ namespace OpenXRVk const OpenXRInteractionUserPathDescriptor* GetUserPathDescriptor(const AZStd::string& userPathName) const; const OpenXRInteractionComponentPathDescriptor* GetCommonComponentPathDescriptor(const AZStd::string& componentPathName) const; + const OpenXRInteractionComponentPathDescriptor* GetComponentPathDescriptor(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, const AZStd::string& componentPathName) const; AZStd::string GetComponentAbsolutePath(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, const AZStd::string& componentPathName) const; //! Unique name across all OpenXRInteractionProfileDescriptor. diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp index ffb3a7085..7f7a913a5 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp @@ -132,11 +132,59 @@ namespace OpenXRVkBuilders // OpenXRInteractionProfilesAsset Support End ///////////////////////////////////////////////////////////////////////////////////// + template + static AZStd::unique_ptr LoadAssetAsUniquePtr(const AZStd::string& filePath) + { + AZ::ObjectStream::FilterDescriptor loadFilter = AZ::ObjectStream::FilterDescriptor(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES); + auto actionSetsAssetPtr = AZ::Utils::LoadObjectFromFile(filePath, nullptr, loadFilter); + if (!actionSetsAssetPtr) + { + return nullptr; + } + return AZStd::unique_ptr(actionSetsAssetPtr); + } + + static AZStd::string GetInteractionProfileAssetSourcePath(const OpenXRVk::OpenXRActionSetsAsset& actionSetsAsset) + { + const auto& sourceUuid = actionSetsAsset.m_interactionProfilesAsset.GetId().m_guid; + bool foundSource = false; + AZ::Data::AssetInfo sourceAssetInfo; + AZStd::string sourceWatchFolder; + AzToolsFramework::AssetSystemRequestBus::BroadcastResult(foundSource, &AzToolsFramework::AssetSystemRequestBus::Events::GetSourceInfoBySourceUUID, + sourceUuid, sourceAssetInfo, sourceWatchFolder); + AZStd::string sourcePath; + if (foundSource) + { + constexpr bool caseInsensitive = false; + constexpr bool normalize = true; + AZ::StringFunc::Path::Join(sourceWatchFolder.c_str(), sourceAssetInfo.m_relativePath.c_str(), sourcePath, caseInsensitive, normalize); + } + return sourcePath; + } + void OpenXRActionSetsAssetBuilder::CreateActionSetsAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { + // Make sure the InteractionProfiles asset referenced in this ActionSets asset exists. and if so, + // also declare job dependency. + auto actionSetsAssetPtr = LoadAssetAsUniquePtr(request.m_sourceFile); + if (!actionSetsAssetPtr) + { + AZ_Error(LogName, false, "Failed to load the ActionSets asset at path[%s].", request.m_sourceFile.c_str()); + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed; + return; + } + + auto interactionProfileSourcePath = GetInteractionProfileAssetSourcePath(*actionSetsAssetPtr.get()); + if (interactionProfileSourcePath.empty()) + { + AZ_Error(LogName, false, "An ActionSets source asset requires a valid InteractionProfiles source asset."); + response.m_result = AssetBuilderSDK::CreateJobsResultCode::Failed; + return; + } + for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms) { - if (platformInfo.m_identifier != "pc") + if (platformInfo.m_identifier != "pc") //FIXME: GALIB REMOVE ME. { continue; } @@ -147,19 +195,257 @@ namespace OpenXRVkBuilders jobDescriptor.m_critical = true; jobDescriptor.m_jobKey = ActionSetsAssetJobKey; jobDescriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str()); + + AssetBuilderSDK::SourceFileDependency sourceFileDependency{}; + sourceFileDependency.m_sourceFileDependencyPath = interactionProfileSourcePath; + auto jobDependency = AssetBuilderSDK::JobDependency(InteractionProfilesAssetJobKey, platformInfo.m_identifier, + AssetBuilderSDK::JobDependencyType::Order, sourceFileDependency); + jobDescriptor.m_jobDependencyList.emplace_back(AZStd::move(jobDependency)); + response.m_createJobOutputs.emplace_back(AZStd::move(jobDescriptor)); } // for all request.m_enabledPlatforms response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } + + // A well formed name string should follow this guideline: + // It should not contains characters which are not allowed in a SINGLE LEVEL of a well-formed path string + // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings + static AZ::Outcome ValidateOpenXRName(const AZStd::string& name) + { + static AZStd::regex s_validCharactersRegEx (R"(^[a-z0-9\-_\.]+$)", AZStd::regex::ECMAScript); + if (!AZStd::regex_match(name, s_validCharactersRegEx) + { + return AZ::Failure( + AZStd::string::format("The name [%s] contains an invalid character", name.c_str()) + ); + } + return AZ::Success(); + } + + + static AZ::Outcome ValidateOpenXRLocalizedName(const AZStd::string& name) + { + (void)name; + return AZ::Failure( + AZStd::string::format("%s NOT IMPLEMENTED", __FUNCTION__) + ); + } + + + static AZ::Outcome ValidateActionPathDescriptor(const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor, + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) + { + if (actionPathDescriptor.m_interactionProfileName.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionPath Descriptor must have an InteractionProfile name.") + ); + } + const auto interactionProfileDescriptorPtr = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); + if (!interactionProfileDescriptorPtr) + { + return AZ::Failure( + AZStd::string::format("Unknown Interaction Profile Descriptor named [%s].", + actionPathDescriptor.m_interactionProfileName.c_str()) + ); + } + + if (actionPathDescriptor.m_userPathName.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionPath Descriptor must have an UserPath name.") + ); + } + const auto userPathDescriptorPtr = interactionProfileDescriptorPtr->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); + if (!userPathDescriptorPtr) + { + return AZ::Failure( + AZStd::string::format("Unknown UserPath descriptor named [%s].", + actionPathDescriptor.m_userPathName.c_str()) + ); + } + + if (actionPathDescriptor.m_componentPathName.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionPath Descriptor must have a ComponentPath name.") + ); + } + const auto componentPathDescriptorPtr = interactionProfileDescriptorPtr->GetComponentPathDescriptor(*userPathDescriptorPtr, actionPathDescriptor.m_componentPathName); + if (!componentPathDescriptorPtr) + { + return AZ::Failure( + AZStd::string::format("Unknown ComponentPath descriptor named [%s].", + actionPathDescriptor.m_componentPathName.c_str()) + ); + } + + return AZ::Success(); + } + + + static const AZStd::string& GetActionTypeStringFromActionPathDescriptor( + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor + ) + { + static const AZStd::string emptyStr; + + const auto interactionProfileDescriptorPtr = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); + if (!interactionProfileDescriptorPtr) + { + return emptyStr; + } + + const auto userPathDescriptorPtr = interactionProfileDescriptorPtr->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); + if (!userPathDescriptorPtr) + { + return emptyStr; + } + const auto componentPathDescriptorPtr = interactionProfileDescriptorPtr->GetComponentPathDescriptor(*userPathDescriptorPtr, actionPathDescriptor.m_componentPathName); + if (!componentPathDescriptorPtr) + { + return emptyStr; + } + return componentPathDescriptorPtr->m_actionTypeStr; + } + + static bool IsActionTypeBoolOrFloat(const AZStd::string& actionTypeStr) + { + return ( + (actionTypeStr == OpenXRVk::OpenXRInteractionComponentPathDescriptor::s_TypeBoolStr) || + (actionTypeStr == OpenXRVk::OpenXRInteractionComponentPathDescriptor::s_TypeFloatStr) + ); + } + + static bool AreCompatibleActionTypeStrings(const AZStd::string& lhs, const AZStd::string& rhs) + { + if (IsActionTypeBoolOrFloat(lhs) && IsActionTypeBoolOrFloat(rhs)) + { + return true; + } + return (lhs == rhs); + } + + + static AZ::Outcome ValidateActionDescriptor( + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRVk::OpenXRActionDescriptor& actionDescriptor) + { + { + auto outcome = ValidateOpenXRName(actionDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate Action Descriptor named=[%s].\nReason:\n%s", + actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + + outcome = ValidateOpenXRLocalizedName(actionDescriptor.m_localizedName); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate localized name of Action Descriptor named=[%s]\nReason:\n%s", + actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + + if (actionDescriptor.m_actionPathDescriptors.empty()) + { + return AZ::Failure( + AZStd::string::format("At least one ActionPath Descriptor is required by Action Descriptor named=[%s]\nReason:\n%s", + actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + // It is very important that all action path descriptors have compatible data types. + const AZStd::string& firstActionTypeStr = GetActionTypeStringFromActionPathDescriptor( + interactionProfilesAsset, actionDescriptor.m_actionPathDescriptors[0]); + uint32_t actionPathIndex = 0; + for (const auto& actionPathDescriptor : actionDescriptor.m_actionPathDescriptors) + { + auto outcome = ValidateActionPathDescriptor(actionPathDescriptor, interactionProfilesAsset); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate Action Path Descriptor for Action Descriptor named=[%s].\nReason:\n%s", + actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + const AZStd::string& actionTypeStr = GetActionTypeStringFromActionPathDescriptor( + interactionProfilesAsset, actionPathDescriptor); + if (!AreCompatibleActionTypeStrings(firstActionTypeStr, actionTypeStr)) + { + return AZ::Failure( + AZStd::string::format("ActionType=[%s] of ActionPath Descriptor[%u] is NOT compatible with the ActionType=[%s] ActionPath Descriptor[0]", + actionTypeStr.c_str(), actionPathIndex, firstActionTypeStr.c_str()) + ); + } + actionPathIndex++; + } + + return AZ::Success(); + } + + + static AZ::Outcome ValidateActionSetsAsset(const OpenXRVk::OpenXRActionSetsAsset& actionSetsAsset, + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) + { + if (actionSetsAsset.m_actionSetDescriptors.empty()) + { + return AZ::Failure("At least one ActionSet must be listed in an ActionSets asset"); + } + + for (const auto& actionSetDescriptor : actionSetsAsset.m_actionSetDescriptors) + { + { + auto outcome = ValidateOpenXRName(actionSetDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", + actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + { + auto outcome = ValidateOpenXRLocalizedName(actionSetDescriptor.m_localizedName); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", + actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + for (const auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors) + { + auto outcome = ValidateActionDescriptor(interactionProfilesAsset, actionDescriptor); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", + actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + } + + return AZ::Success(); + } + void OpenXRActionSetsAssetBuilder::ProcessActionSetsAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const { - AZ::ObjectStream::FilterDescriptor loadFilter = AZ::ObjectStream::FilterDescriptor(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES); - auto actionSetsAssetPtr = AZ::Utils::LoadObjectFromFile(request.m_fullPath, nullptr, loadFilter); + auto actionSetsAssetPtr = LoadAssetAsUniquePtr(request.m_fullPath); if (!actionSetsAssetPtr) { - AZ_Error(LogName, false, "Failed to LoadObjectFromFile %s", request.m_fullPath.c_str()); + AZ_Error(LogName, false, "Failed to Load ActionsSet asset from File %s", request.m_fullPath.c_str()); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; return; } @@ -168,7 +454,29 @@ namespace OpenXRVkBuilders // to construct the data in it. Because we are running in a builder context, the OpenXRInteractionProfilesAsset // is loaded with a null handle, BUT the AssetHint is valid and we'll use the AssetHint to discover // the OpenXRInteractionProfilesAsset and load it manually. + auto interactionProfileSourcePath = GetInteractionProfileAssetSourcePath(*actionSetsAssetPtr.get()); + if (interactionProfileSourcePath.empty()) + { + AZ_Error(LogName, false, "An ActionSets source asset requires a valid InteractionProfiles source asset."); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } + auto interactionProfileAssetPtr = LoadAssetAsUniquePtr(interactionProfileSourcePath); + if (!interactionProfileAssetPtr) + { + AZ_Error(LogName, false, "Failed to Load InteractionProfiles asset from File %s", interactionProfileSourcePath.c_str()); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } + auto outcome = ValidateActionSetsAsset(*actionSetsAssetPtr.get(), *interactionProfileAssetPtr.get()); + if (!outcome.IsSuccess()) + { + AZ_Error(LogName, false, "Invalid source ActionSets content when using source InteractionProfiles asset file [%s]. Reason:\n[%s]", + interactionProfileSourcePath.c_str(), outcome.GetError().c_str()); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } // We keep exact same asset name and extension. AZStd::string assetFileName; @@ -178,7 +486,7 @@ namespace OpenXRVkBuilders AZStd::string assetOutputPath; AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), assetFileName.c_str(), assetOutputPath, true); - bool result = AZ::Utils::SaveObjectToFile(assetOutputPath, AZ::DataStream::ST_XML, actionSetsAssetPtr); + bool result = AZ::Utils::SaveObjectToFile(assetOutputPath, AZ::DataStream::ST_XML, actionSetsAssetPtr.get()); if (result == false) { AZ_Error(LogName, false, "Failed to save asset to %s", assetOutputPath.c_str()); @@ -186,11 +494,11 @@ namespace OpenXRVkBuilders return; } - // This step is very important, because it declares product dependency between ShaderAsset and the root ShaderVariantAssets (one for each supervariant). - // This will guarantee that when the ShaderAsset is loaded at runtime, the ShaderAsset will report OnAssetReady only after the root ShaderVariantAssets - // are already fully loaded and ready. + // This step is very important, because it declares that this OpenXRActionsSetAsset depends on a OpenXRInteractionProfilesAsset. + // This will guarantee that when the OpenXRActionsSetAsset is loaded at runtime, the Asset Catalog will report OnAssetReady + // only after the OpenXRInteractionProfilesAsset is already fully loaded and ready. AssetBuilderSDK::JobProduct jobProduct; - if (!AssetBuilderSDK::OutputObject(actionSetsAssetPtr, assetOutputPath, azrtti_typeid(), + if (!AssetBuilderSDK::OutputObject(actionSetsAssetPtr.get(), assetOutputPath, azrtti_typeid(), aznumeric_cast(0), jobProduct)) { AZ_Error(LogName, false, "FIXME this message."); diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp index 43af7e544..b78ef12d1 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp @@ -9,7 +9,7 @@ #include #include -#include "OpenXRAssetBuildersSystemComponent.h" +#include "OpenXRVkAssetBuildersSystemComponent.h" namespace OpenXRVkBuilders { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp index ac265cea2..ff43f6bf6 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp @@ -232,19 +232,26 @@ namespace OpenXRVk return nullptr; } - AZStd::string OpenXRInteractionProfileDescriptor::GetComponentAbsolutePath(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, + const OpenXRInteractionComponentPathDescriptor* OpenXRInteractionProfileDescriptor::GetComponentPathDescriptor(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, const AZStd::string& componentPathName) const { - // First check if the user path owns the component path, if not, search in the common components list. auto componentPathDescriptor = userPathDescriptor.GetComponentPathDescriptor(componentPathName); if (!componentPathDescriptor) { // Look in common paths - componentPathDescriptor = GetCommonComponentPathDescriptor(componentPathName); - if (!componentPathDescriptor) - { - return {}; - } + return GetCommonComponentPathDescriptor(componentPathName); + } + return componentPathDescriptor; + } + + AZStd::string OpenXRInteractionProfileDescriptor::GetComponentAbsolutePath(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, + const AZStd::string& componentPathName) const + { + // First check if the user path owns the component path, if not, search in the common components list. + auto componentPathDescriptor = GetComponentPathDescriptor(userPathDescriptor, componentPathName); + if (!componentPathDescriptor) + { + return {}; } return userPathDescriptor.m_path + componentPathDescriptor->m_path; } From e30cf21bae8c02ddc162e63f46cf9f9a89637c0b Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Mon, 29 Jan 2024 04:55:33 -0600 Subject: [PATCH 23/33] Saving thw work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkActionSetsAsset.h | 2 - .../OpenXRVkInteractionProfilesAsset.h | 29 +- .../OpenXRVkActionSetsAssetBuilder.cpp | 282 +++++++++++++++++- .../Builders/OpenXRVkActionSetsAssetBuilder.h | 4 +- .../Code/Source/OpenXRVkActionSetsAsset.cpp | 36 --- .../OpenXRVkInteractionProfilesAsset.cpp | 79 ++--- 6 files changed, 328 insertions(+), 104 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h index c1a779157..e98de4786 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h @@ -87,8 +87,6 @@ namespace OpenXRVk AZ_RTTI(OpenXRActionSetsAsset, "{C2DEE370-6151-4701-AEA5-AEA3CA247CFF}", AZ::Data::AssetData); static void Reflect(AZ::ReflectContext* context); - OpenXRActionSetsAsset(); - static constexpr char s_assetTypeName[] = "OpenXR Action Sets Asset"; static constexpr char s_assetExtension[] = "xractions"; diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h index 4b3e354b8..382788ddf 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h @@ -125,20 +125,21 @@ namespace OpenXRVk AZStd::vector m_interactionProfileDescriptors; }; - //! Custom asset handler - class OpenXRInteractionProfilesAssetHandler final - : public AzFramework::GenericAssetHandler - { - public: - AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); - AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); - - static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; - - OpenXRInteractionProfilesAssetHandler(); - - bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; - }; + // REMOVEME GALIB FIXME + // //! Custom asset handler + // class OpenXRInteractionProfilesAssetHandler final + // : public AzFramework::GenericAssetHandler + // { + // public: + // AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); + // AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); + // + // static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; + // + // OpenXRInteractionProfilesAssetHandler(); + // + // bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; + // }; }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp index 7f7a913a5..8025f5244 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp @@ -18,6 +18,44 @@ namespace OpenXRVkBuilders { + // Unlike an OpenXR Name, a Localized Name is just a utf-8 string, it can contain upper case letters + // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. + static AZ::Outcome ValidateName(const AZStd::string& name) + { + if (name.empty()) + { + return AZ::Failure("Name should not be empty."); + } + //Spaces at the beginning and end of the name are not allowed. + AZStd::string tmpName(name); + AZ::StringFunc::TrimWhiteSpace(tmpName, true, true); + if (tmpName.empty()) + { + return AZ::Failure("Name is just a bunch of spaces."); + } + if (tmpName.size() != name.size()) + { + return AZ::Failure( + AZStd::string::format("Trailing or leading spaces are not allowed in a Name [%s].", + name.c_str()) + ); + } + return AZ::Success(); + } + + // Unlike an OpenXR Name, a Localized Name is just a utf-8 string, it can contain upper case letters + // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. + static AZ::Outcome ValidateOpenXRLocalizedName(const AZStd::string& name) + { + auto outcome = ValidateName(name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Localized Name [%s] is invalid. Reason:\n%s", name.c_str(), outcome.GetError().c_str()) + ); + } + } + // [[maybe_unused]] const char* AnyAssetBuilderName = "AnyAssetBuilder"; // const char* AnyAssetBuilderJobKey = "Any Asset Builder"; // const char* AnyAssetBuilderDefaultExtension = "azasset"; @@ -90,6 +128,213 @@ namespace OpenXRVkBuilders response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } + static AZ::Outcome ValidateActionTypeString(const AZStd::string& actionTypeStr) + { + static const AZStd::unordered_set ValidActionTypes { + + } + } + + static AZ::Outcome ValidateComponentPathDescriptor(const OpenXRVk::OpenXRInteractionComponentPathDescriptor& componentPathDescriptor, + AZStd::unordered_set& uniqueComponentPathNames, AZStd::unordered_set& uniqueComponentPathPaths) + { + { + if (uniqueComponentPathNames.contains(componentPathDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("A Component Path with name [%s] already exists.", + componentPathDescriptor.m_name.c_str()) + ); + } + uniqueComponentPathNames.emplace(componentPathDescriptor.m_name); + auto outcome = ValidateName(componentPathDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Component Name[% s] is invalid.Reason:\n % s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + { + if (uniqueComponentPathPaths.contains(componentPathDescriptor.m_path)) + { + return AZ::Failure( + AZStd::string::format("A Component Path with path [%s] already exists.", + componentPathDescriptor.m_path.c_str()) + ); + } + uniqueComponentPathPaths.emplace(componentPathDescriptor.m_path); + auto outcome = ValidateOpenXRPath(componentPathDescriptor.m_path); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Component Path path [%s] is invalid. Reason:\n%s", componentPathDescriptor.m_path.c_str(), outcome.GetError().c_str()) + ); + } + } + + auto outcome = ValidateActionTypeString(componentPathDescriptor.m_actionTypeStr); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Component Path path [%s] has an invalid action type. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + + return AZ::Success() + } + + + static AZ::Outcome ValidateUserPathDescriptor(const OpenXRVk::OpenXRInteractionUserPathDescriptor& userPathDescriptor, + AZStd::unordered_set& uniqueUserPathNames, AZStd::unordered_set& uniqueUserPathPaths, + AZStd::unordered_set& uniqueComponentPathNames, AZStd::unordered_set& uniqueComponentPathPaths) + { + { + if (uniqueUserPathNames.contains(userPathDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("An User Path with name [%s] already exists.", + userPathDescriptor.m_name.c_str()) + ); + } + uniqueUserPathNames.emplace(userPathDescriptor.m_name); + auto outcome = ValidateName(userPathDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("User Path Name [%s] is invalid. Reason:\n%s", userPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + { + if (uniqueUserPathPaths.contains(userPathDescriptor.m_path)) + { + return AZ::Failure( + AZStd::string::format("An User Path with path [%s] already exists.", + userPathDescriptor.m_path.c_str()) + ); + } + uniqueUserPathPaths.emplace(userPathDescriptor.m_path); + auto outcome = ValidateOpenXRPath(userPathDescriptor.m_path); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("User Path path [%s] is invalid. Reason:\n%s", userPathDescriptor.m_path.c_str(), outcome.GetError().c_str()) + ); + } + } + for (const auto& componentPathDescriptor : userPathDescriptor.m_componentPathDescriptors) + { + auto outcome = ValidateComponentPathDescriptor(componentPathDescriptor, + uniqueComponentPathNames, uniqueComponentPathPaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Invalid Component Path [%s]. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + return AZ::Success(); + } + + + static AZ::Outcome ValidateInteractionProfileDescriptor( + const OpenXRVk::OpenXRInteractionProfileDescriptor& interactionProfileDescriptor, + AZStd::unordered_set& uniqueNames, AZStd::unordered_set& uniquePaths) + { + { + if (uniqueNames.contains(interactionProfileDescriptor.m_uniqueName)) + { + return AZ::Failure( + AZStd::string::format("An Interaction Profile with name [%s] already exists.", + interactionProfileDescriptor.m_uniqueName.c_str()) + ); + } + uniqueNames.emplace(interactionProfileDescriptor.m_uniqueName); + auto outcome = ValidateName(interactionProfileDescriptor.m_uniqueName); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Interaction Profile Unique Name [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_uniqueName.c_str(), outcome.GetError().c_str()) + ); + } + } + + { + if (uniquePaths.contains(interactionProfileDescriptor.m_path)) + { + return AZ::Failure( + AZStd::string::format("An Interaction Profile with path [%s] already exists.", + interactionProfileDescriptor.m_path.c_str()) + ); + } + uniquePaths.emplace(interactionProfileDescriptor.m_path); + auto outcome = ValidateOpenXRPath(interactionProfileDescriptor.m_path); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Interaction Profile Path [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_path.c_str(), outcome.GetError().c_str()) + ); + } + } + + AZStd::unordered_set uniqueUserPathNames; + AZStd::unordered_set uniqueUserPathPaths; + AZStd::unordered_set uniqueComponentPathNames; + AZStd::unordered_set uniqueComponentPathPaths; + for (const auto& userPathDescriptor : interactionProfileDescriptor.m_userPathDescriptors) + { + auto outcome = ValidateUserPathDescriptor(userPathDescriptor, + uniqueUserPathNames, uniqueUserPathPaths, uniqueComponentPathNames, uniqueComponentPathPaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Invalid User Path [%s]. Reason:\n%s", userPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + for (const auto& componentPathDescriptor : interactionProfileDescriptor.m_commonComponentPathDescriptors) + { + auto outcome = ValidateComponentPathDescriptor(componentPathDescriptor, + uniqueComponentPathNames, uniqueComponentPathPaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Invalid Common Component Path [%s]. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + return AZ::Success(); + } + + + static AZ::Outcome ValidateInteractionProfilesAsset( + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) + { + if (interactionProfilesAsset.m_interactionProfileDescriptors.empty()) + { + return AZ::Failure("An InteractionProfiles asset requires at least one Interaction Profile"); + } + + uint32_t i = 0; + for (const auto& interactionProfileDescriptor : interactionProfilesAsset.m_interactionProfileDescriptors) + { + auto outcome = ValidateInteractionProfileDescriptor(interactionProfileDescriptor); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("InteractionProfile[%u] is invalid. Reason:\n%s", + i, outcome.GetError().c_str()) + ); + } + i++; + } + return AZ::Success(); + } + void OpenXRActionSetsAssetBuilder::ProcessInteractionProfilesAssetJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const { // Open the file, and make sure there's no redundant data, the OpenXR Paths are well formatted, etc. @@ -209,8 +454,7 @@ namespace OpenXRVkBuilders } - // A well formed name string should follow this guideline: - // It should not contains characters which are not allowed in a SINGLE LEVEL of a well-formed path string + // An OpenXR name string only contain characters which are allowed in a SINGLE LEVEL of a well-formed path string // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings static AZ::Outcome ValidateOpenXRName(const AZStd::string& name) { @@ -224,15 +468,6 @@ namespace OpenXRVkBuilders return AZ::Success(); } - - static AZ::Outcome ValidateOpenXRLocalizedName(const AZStd::string& name) - { - (void)name; - return AZ::Failure( - AZStd::string::format("%s NOT IMPLEMENTED", __FUNCTION__) - ); - } - static AZ::Outcome ValidateActionPathDescriptor(const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor, const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) @@ -440,6 +675,29 @@ namespace OpenXRVkBuilders return AZ::Success(); } + //! Each action in an actionSet has a "name" and a "localizedName". The "name" can never be empty, but + //! if "localizedName" is empty we automatically patch it as an identical copy of "name". + static void FixEmptyLocalizedNames(OpenXRVk::OpenXRActionSetsAsset& actionSetAsset) + { + for (auto& actionSetDescriptor : actionSetAsset.m_actionSetDescriptors) + { + if (actionSetDescriptor.m_localizedName.empty()) + { + AZ_Printf(OpenXRActionSetsAssetBuilder::LogName, "ActionSet had empty LocalizedName. Taking new value of [%s]", actionSetDescriptor.m_name.c_str()); + actionSetDescriptor.m_localizedName = actionSetDescriptor.m_name; + } + for (auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors) + { + if (actionDescriptor.m_localizedName.empty()) + { + AZ_Printf(OpenXRActionSetsAssetBuilder::LogName, "Action in ActionSet [%s] had empty LocalizedName. Taking new value of [%s]", + actionSetDescriptor.m_name.c_str(), actionDescriptor.m_name.c_str()); + actionDescriptor.m_localizedName = actionDescriptor.m_name; + } + } + } + } + void OpenXRActionSetsAssetBuilder::ProcessActionSetsAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const { auto actionSetsAssetPtr = LoadAssetAsUniquePtr(request.m_fullPath); @@ -450,6 +708,8 @@ namespace OpenXRVkBuilders return; } + FixEmptyLocalizedNames(*actionSetsAssetPtr.get()); + // The Action Sets Asset contains an asset reference to the OpenXRInteractionProfilesAsset that was used // to construct the data in it. Because we are running in a builder context, the OpenXRInteractionProfilesAsset // is loaded with a null handle, BUT the AssetHint is valid and we'll use the AssetHint to discover diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h index 0e5c927bc..55ceef4bd 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h @@ -19,6 +19,8 @@ namespace OpenXRVkBuilders public: AZ_TYPE_INFO(OpenXRActionSetsAssetBuilder, "{1D053000-7799-459D-B99B-FF6AE6394BC1}"); + static constexpr char LogName[] = "OpenXRActionSetsAssetBuilder"; + static constexpr const char* InteractionProfilesAssetJobKey = "XR Interaction Profiles Asset"; static constexpr const char* ActionSetsAssetJobKey = "XR Action Sets Asset"; @@ -35,8 +37,6 @@ namespace OpenXRVkBuilders private: AZ_DISABLE_COPY_MOVE(OpenXRActionSetsAssetBuilder); - static constexpr char LogName[] = "OpenXRActionSetsAssetBuilder"; - void CreateInteractionProfilesAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; void ProcessInteractionProfilesAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp index 05c971384..6b4ff9b28 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp @@ -116,13 +116,6 @@ namespace OpenXRVk AZStd::vector OpenXRActionPathDescriptor::GetInteractionProfiles() const { - // auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); - // if (!interactionProviderIface) - // { - // return {}; - // } - // return interactionProviderIface->GetInteractionProfileNames(); - const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset(); if (!interactionProfilesAsset.IsReady()) { @@ -160,25 +153,6 @@ namespace OpenXRVk retList.push_back(userPathDescriptor.m_name); } return retList; - - - // AZStd::vector retList; - // auto interactionProviderIface = OpenXRInteractionProfilesProviderInterface::Get(); - // if (!interactionProviderIface) - // { - // return retList; - // } - // - // const auto * profileDescriptor = interactionProviderIface->GetInteractionProfileDescriptor(m_interactionProfileName); - // if (!profileDescriptor) - // { - // return retList; - // } - // for (const auto& userPathDescriptor : profileDescriptor->m_userPathDescriptors) - // { - // retList.push_back(userPathDescriptor.m_name); - // } - // return retList; } AZ::Crc32 OpenXRActionPathDescriptor::OnComponentPathSelected() @@ -346,16 +320,6 @@ namespace OpenXRVk } } - OpenXRActionSetsAsset::OpenXRActionSetsAsset() : AZ::Data::AssetData() - { - // The reason we don't load the interaction profiles asset upon construction - // is because we only want to load the singleton asset only if we are 100% sure - // this asset is being edited by the Asset Editor. - // Remember that at game runtime, there's no such thing as the Asset Editor. - //constexpr bool loadAsset = false; - //EditorInternal::SetCurrentInteractionProfilesAsset(m_interactionProfilesAsset, loadAsset); - } - AZ::Crc32 OpenXRActionSetsAsset::OnInteractionProfilesAssetChanged() { EditorInternal::SetCurrentInteractionProfilesAsset(m_interactionProfilesAsset); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp index ff43f6bf6..fb8a3d766 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp @@ -302,44 +302,45 @@ namespace OpenXRVk /// OpenXRInteractionProfilesAsset /////////////////////////////////////////////////////////// - OpenXRInteractionProfilesAssetHandler::OpenXRInteractionProfilesAssetHandler() - : AzFramework::GenericAssetHandler( - OpenXRInteractionProfilesAsset::s_assetTypeName, - "Other", - OpenXRInteractionProfilesAsset::s_assetExtension) - { - } - - bool OpenXRInteractionProfilesAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) - { - auto profileAsset = asset.GetAs(); - if (!profileAsset) - { - AZ_Error(LogName, false, "This should be an OpenXR Interaction Profile Asset, as this is the only type this handler can process."); - return false; - } - const auto& descriptorsList = profileAsset->m_interactionProfileDescriptors; - if (descriptorsList.empty()) - { - AZ_Error(LogName, false, "The list of Interaction Profile Descriptors is empty."); - return false; - } - - if (!m_serializeContext) - { - AZ_Error(LogName, false, "Can't save the OpenXR Interaction Profile Asset without a serialize context."); - return false; - } - - for (const auto& profileDescriptor : descriptorsList) - { - if (!profileDescriptor.Validate()) - { - return false; - } - } - return AZ::Utils::SaveObjectToStream(*stream, AZ::ObjectStream::ST_JSON, profileAsset, - asset->RTTI_GetType(), m_serializeContext); - } + // REMOVEME FIXME GALIB + // OpenXRInteractionProfilesAssetHandler::OpenXRInteractionProfilesAssetHandler() + // : AzFramework::GenericAssetHandler( + // OpenXRInteractionProfilesAsset::s_assetTypeName, + // "Other", + // OpenXRInteractionProfilesAsset::s_assetExtension) + // { + // } + // + // bool OpenXRInteractionProfilesAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) + // { + // auto profileAsset = asset.GetAs(); + // if (!profileAsset) + // { + // AZ_Error(LogName, false, "This should be an OpenXR Interaction Profile Asset, as this is the only type this handler can process."); + // return false; + // } + // const auto& descriptorsList = profileAsset->m_interactionProfileDescriptors; + // if (descriptorsList.empty()) + // { + // AZ_Error(LogName, false, "The list of Interaction Profile Descriptors is empty."); + // return false; + // } + // + // if (!m_serializeContext) + // { + // AZ_Error(LogName, false, "Can't save the OpenXR Interaction Profile Asset without a serialize context."); + // return false; + // } + // + // for (const auto& profileDescriptor : descriptorsList) + // { + // if (!profileDescriptor.Validate()) + // { + // return false; + // } + // } + // return AZ::Utils::SaveObjectToStream(*stream, AZ::ObjectStream::ST_JSON, profileAsset, + // asset->RTTI_GetType(), m_serializeContext); + // } } // namespace OpenXRVk From 668e8efddf44dd1b76057fe2aa6ee9cc8459255e Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:07:53 -0600 Subject: [PATCH 24/33] Saving the work. 99% done with asset validation. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkActionSetsAsset.h | 6 +- .../OpenXRVk/OpenXRVkSystemComponent.h | 4 +- .../OpenXRVkActionSetsAssetBuilder.cpp | 209 ++++++++++++++---- .../OpenXRVkInteractionProfilesAsset.cpp | 41 ---- .../Code/Source/OpenXRVkSystemComponent.cpp | 7 +- 5 files changed, 171 insertions(+), 96 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h index e98de4786..201911f91 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h @@ -98,9 +98,9 @@ namespace OpenXRVk }; //! We need a custom asset handler because OpenXRActionSetsAsset contains a reference to another - //! asset of type OpenXRInteractionProfilesAsset, and we don't have a custom builder that declares the - //! dependency in the Asset Catalog database. Without this custom handler, the engine asserts - //! complaining about the missing dependency. + //! asset of type OpenXRInteractionProfilesAsset and we need to set a static singleton of type + //! OpenXRInteractionProfilesAsset when the user is creating/editing an OpenXRActionSetsAsset with + //! the Asset Editor. class OpenXRActionSetsAssetHandler final : public AzFramework::GenericAssetHandler { diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h index 01e23acf8..f7b2e822b 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSystemComponent.h @@ -68,7 +68,7 @@ namespace OpenXRVk private: XR::Ptr m_instance; - AZStd::unique_ptr m_interactionProfilesAssetHandler; - AZStd::unique_ptr> m_actionSetsAssetHandler; + AZStd::unique_ptr> m_interactionProfilesAssetHandler; + AZStd::unique_ptr m_actionSetsAssetHandler; }; } \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp index 8025f5244..589379da9 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp @@ -18,7 +18,19 @@ namespace OpenXRVkBuilders { - // Unlike an OpenXR Name, a Localized Name is just a utf-8 string, it can contain upper case letters + template + static AZStd::unique_ptr LoadAssetAsUniquePtr(const AZStd::string& filePath) + { + AZ::ObjectStream::FilterDescriptor loadFilter = AZ::ObjectStream::FilterDescriptor(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES); + auto actionSetsAssetPtr = AZ::Utils::LoadObjectFromFile(filePath, nullptr, loadFilter); + if (!actionSetsAssetPtr) + { + return nullptr; + } + return AZStd::unique_ptr(actionSetsAssetPtr); + } + + // A regular Name is just a utf-8 string, it can contain upper case letters // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. static AZ::Outcome ValidateName(const AZStd::string& name) { @@ -43,6 +55,7 @@ namespace OpenXRVkBuilders return AZ::Success(); } + // Unlike an OpenXR Name, a Localized Name is just a utf-8 string, it can contain upper case letters // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. static AZ::Outcome ValidateOpenXRLocalizedName(const AZStd::string& name) @@ -54,18 +67,9 @@ namespace OpenXRVkBuilders AZStd::string::format("Localized Name [%s] is invalid. Reason:\n%s", name.c_str(), outcome.GetError().c_str()) ); } + return AZ::Success(); } - // [[maybe_unused]] const char* AnyAssetBuilderName = "AnyAssetBuilder"; - // const char* AnyAssetBuilderJobKey = "Any Asset Builder"; - // const char* AnyAssetBuilderDefaultExtension = "azasset"; - // const char* AnyAssetSourceExtensions[] = - // { - // "azasset", - // "attimage", - // "azbuffer", - // }; - // const uint32_t NumberOfSourceExtensions = AZ_ARRAY_SIZE(AnyAssetSourceExtensions); void OpenXRActionSetsAssetBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { @@ -130,9 +134,52 @@ namespace OpenXRVkBuilders static AZ::Outcome ValidateActionTypeString(const AZStd::string& actionTypeStr) { - static const AZStd::unordered_set ValidActionTypes { + using CPD = OpenXRVk::OpenXRInteractionComponentPathDescriptor; + static const AZStd::unordered_set ValidActionTypes{ + {CPD::s_TypeBoolStr}, + {CPD::s_TypeFloatStr}, + {CPD::s_TypeVector2Str}, + {CPD::s_TypePoseStr}, + {CPD::s_TypeVibrationStr} + }; + + if (!ValidActionTypes.contains(actionTypeStr)) + { + static AZStd::string ValidListStr; + if (ValidListStr.empty()) + { + ValidListStr += "[ "; + for (const auto& validActionTypeStr : ValidActionTypes) + { + if (!ValidListStr.empty()) + { + ValidListStr += ", "; + } + ValidListStr += validActionTypeStr; + } + ValidListStr += " ]"; + } + return AZ::Failure( + AZStd::string::format("Action Type [%s] is invalid. It can only be one of %s", + actionTypeStr.c_str(), ValidListStr.c_str()) + ); + } + + return AZ::Success(); + } + // An OpenXR path string only contain characters as described here + // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings + static AZ::Outcome ValidateOpenXRPath(const AZStd::string& path) + { + static AZStd::regex s_validCharactersRegEx(R"(^(/[a-z0-9\-_\.]+)+$)", AZStd::regex::ECMAScript); + if (!AZStd::regex_match(path, s_validCharactersRegEx)) + { + return AZ::Failure( + AZStd::string::format("The path [%s] contains an invalid character, or is missing a leading '/' or contains a leading '/'", path.c_str()) + ); } + return AZ::Success(); } static AZ::Outcome ValidateComponentPathDescriptor(const OpenXRVk::OpenXRInteractionComponentPathDescriptor& componentPathDescriptor, @@ -181,7 +228,7 @@ namespace OpenXRVkBuilders ); } - return AZ::Success() + return AZ::Success(); } @@ -319,10 +366,13 @@ namespace OpenXRVkBuilders return AZ::Failure("An InteractionProfiles asset requires at least one Interaction Profile"); } + AZStd::unordered_set uniqueNames; + AZStd::unordered_set uniquePaths; uint32_t i = 0; for (const auto& interactionProfileDescriptor : interactionProfilesAsset.m_interactionProfileDescriptors) { - auto outcome = ValidateInteractionProfileDescriptor(interactionProfileDescriptor); + auto outcome = ValidateInteractionProfileDescriptor(interactionProfileDescriptor, + uniqueNames, uniquePaths); if (!outcome.IsSuccess()) { return AZ::Failure( @@ -338,11 +388,22 @@ namespace OpenXRVkBuilders void OpenXRActionSetsAssetBuilder::ProcessInteractionProfilesAssetJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const { // Open the file, and make sure there's no redundant data, the OpenXR Paths are well formatted, etc. - auto interactionProfilesAssetPtr = AZ::Utils::LoadObjectFromFile(request.m_fullPath); - //AZ_Error(LogName, false, "The interaction profiles contain %zu profiles", interactionProfilesAssetPtr->m_interactionProfileDescriptors.size()); + auto interactionProfilesAssetPtr = LoadAssetAsUniquePtr(request.m_fullPath); + if (!interactionProfilesAssetPtr) + { + AZ_Error(LogName, false, "Failed to load interaction profile source asset [%s]", request.m_fullPath.c_str()); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } - // FIXME: TODO: All this builder is supposed to do is validate the data. - // If the validation passes, then we simply generate a product which is just a copy of the original. + auto outcome = ValidateInteractionProfilesAsset(*interactionProfilesAssetPtr.get()); + if (!outcome.IsSuccess()) + { + AZ_Error(LogName, false, "Invalid InteractionProfilesAsset [%s]. Reason:\n%s", + request.m_fullPath.c_str(), outcome.GetError().c_str()); + response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; + return; + } // We keep exact same asset name and extension. AZStd::string assetFileName; @@ -352,7 +413,7 @@ namespace OpenXRVkBuilders AZStd::string assetOutputPath; AzFramework::StringFunc::Path::ConstructFull(request.m_tempDirPath.c_str(), assetFileName.c_str(), assetOutputPath, true); - bool result = AZ::Utils::SaveObjectToFile(assetOutputPath, AZ::DataStream::ST_XML, interactionProfilesAssetPtr); + bool result = AZ::Utils::SaveObjectToFile(assetOutputPath, AZ::DataStream::ST_XML, interactionProfilesAssetPtr.get()); if (result == false) { AZ_Error(LogName, false, "Failed to save asset to %s", assetOutputPath.c_str()); @@ -360,11 +421,9 @@ namespace OpenXRVkBuilders return; } - // This step is very important, because it declares product dependency between ShaderAsset and the root ShaderVariantAssets (one for each supervariant). - // This will guarantee that when the ShaderAsset is loaded at runtime, the ShaderAsset will report OnAssetReady only after the root ShaderVariantAssets - // are already fully loaded and ready. AssetBuilderSDK::JobProduct jobProduct; - if (!AssetBuilderSDK::OutputObject(interactionProfilesAssetPtr, assetOutputPath, azrtti_typeid(), + if (!AssetBuilderSDK::OutputObject(interactionProfilesAssetPtr.get(), assetOutputPath, + azrtti_typeid(), aznumeric_cast(0), jobProduct)) { AZ_Error(LogName, false, "FIXME this message."); @@ -377,17 +436,6 @@ namespace OpenXRVkBuilders // OpenXRInteractionProfilesAsset Support End ///////////////////////////////////////////////////////////////////////////////////// - template - static AZStd::unique_ptr LoadAssetAsUniquePtr(const AZStd::string& filePath) - { - AZ::ObjectStream::FilterDescriptor loadFilter = AZ::ObjectStream::FilterDescriptor(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES); - auto actionSetsAssetPtr = AZ::Utils::LoadObjectFromFile(filePath, nullptr, loadFilter); - if (!actionSetsAssetPtr) - { - return nullptr; - } - return AZStd::unique_ptr(actionSetsAssetPtr); - } static AZStd::string GetInteractionProfileAssetSourcePath(const OpenXRVk::OpenXRActionSetsAsset& actionSetsAsset) { @@ -459,7 +507,7 @@ namespace OpenXRVkBuilders static AZ::Outcome ValidateOpenXRName(const AZStd::string& name) { static AZStd::regex s_validCharactersRegEx (R"(^[a-z0-9\-_\.]+$)", AZStd::regex::ECMAScript); - if (!AZStd::regex_match(name, s_validCharactersRegEx) + if (!AZStd::regex_match(name, s_validCharactersRegEx)) { return AZ::Failure( AZStd::string::format("The name [%s] contains an invalid character", name.c_str()) @@ -470,8 +518,23 @@ namespace OpenXRVkBuilders static AZ::Outcome ValidateActionPathDescriptor(const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor, - const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, + AZStd::unordered_set& uniqueActionPaths) { + auto concatenatedActionPath = actionPathDescriptor.m_interactionProfileName + + actionPathDescriptor.m_userPathName + + actionPathDescriptor.m_componentPathName; + if (uniqueActionPaths.contains(concatenatedActionPath)) + { + return AZ::Failure( + AZStd::string::format("An Action Path with profile[%s], userPath[%s], componentPath[%s] already exists.", + actionPathDescriptor.m_interactionProfileName.c_str(), + actionPathDescriptor.m_userPathName.c_str(), + actionPathDescriptor.m_componentPathName.c_str()) + ); + } + uniqueActionPaths.emplace(AZStd::move(concatenatedActionPath)); + if (actionPathDescriptor.m_interactionProfileName.empty()) { return AZ::Failure( @@ -567,9 +630,19 @@ namespace OpenXRVkBuilders static AZ::Outcome ValidateActionDescriptor( const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, - const OpenXRVk::OpenXRActionDescriptor& actionDescriptor) + const OpenXRVk::OpenXRActionDescriptor& actionDescriptor, + AZStd::unordered_set& uniqueActionNames, + AZStd::unordered_set& uniqueActionLocalizedNames) { { + if (uniqueActionNames.contains(actionDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("An Action with name [%s] already exists.", + actionDescriptor.m_name.c_str()) + ); + } + uniqueActionNames.emplace(actionDescriptor.m_name); auto outcome = ValidateOpenXRName(actionDescriptor.m_name); if (!outcome.IsSuccess()) { @@ -578,24 +651,37 @@ namespace OpenXRVkBuilders actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) ); } + } - outcome = ValidateOpenXRLocalizedName(actionDescriptor.m_localizedName); - if (!outcome.IsSuccess()) + { + if (uniqueActionLocalizedNames.contains(actionDescriptor.m_localizedName)) { return AZ::Failure( - AZStd::string::format("Failed to validate localized name of Action Descriptor named=[%s]\nReason:\n%s", - actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + AZStd::string::format("An Action with localized name [%s] already exists.", + actionDescriptor.m_localizedName.c_str()) ); } - - if (actionDescriptor.m_actionPathDescriptors.empty()) + uniqueActionLocalizedNames.emplace(actionDescriptor.m_localizedName); + auto outcome = ValidateOpenXRLocalizedName(actionDescriptor.m_localizedName); + if (!outcome.IsSuccess()) { return AZ::Failure( - AZStd::string::format("At least one ActionPath Descriptor is required by Action Descriptor named=[%s]\nReason:\n%s", + AZStd::string::format("Failed to validate localized name of Action Descriptor named=[%s]\nReason:\n%s", actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) ); } } + + + if (actionDescriptor.m_actionPathDescriptors.empty()) + { + return AZ::Failure( + AZStd::string::format("At least one ActionPath Descriptor is required by Action Descriptor named=[%s].\n", + actionDescriptor.m_name.c_str()) + ); + } + + AZStd::unordered_set uniqueActionPaths; // It is very important that all action path descriptors have compatible data types. const AZStd::string& firstActionTypeStr = GetActionTypeStringFromActionPathDescriptor( @@ -603,7 +689,7 @@ namespace OpenXRVkBuilders uint32_t actionPathIndex = 0; for (const auto& actionPathDescriptor : actionDescriptor.m_actionPathDescriptors) { - auto outcome = ValidateActionPathDescriptor(actionPathDescriptor, interactionProfilesAsset); + auto outcome = ValidateActionPathDescriptor(actionPathDescriptor, interactionProfilesAsset, uniqueActionPaths); if (!outcome.IsSuccess()) { return AZ::Failure( @@ -635,9 +721,19 @@ namespace OpenXRVkBuilders return AZ::Failure("At least one ActionSet must be listed in an ActionSets asset"); } + AZStd::unordered_set uniqueActionSetNames; + AZStd::unordered_set uniqueActionSetLocalizedNames; for (const auto& actionSetDescriptor : actionSetsAsset.m_actionSetDescriptors) { { + if (uniqueActionSetNames.contains(actionSetDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("An ActionSet named=[%s] already exists.", + actionSetDescriptor.m_name.c_str()) + ); + } + uniqueActionSetNames.emplace(actionSetDescriptor.m_name); auto outcome = ValidateOpenXRName(actionSetDescriptor.m_name); if (!outcome.IsSuccess()) { @@ -649,6 +745,14 @@ namespace OpenXRVkBuilders } { + if (uniqueActionSetLocalizedNames.contains(actionSetDescriptor.m_localizedName)) + { + return AZ::Failure( + AZStd::string::format("An ActionSet with localized named=[%s] already exists.", + actionSetDescriptor.m_localizedName.c_str()) + ); + } + uniqueActionSetLocalizedNames.emplace(actionSetDescriptor.m_localizedName); auto outcome = ValidateOpenXRLocalizedName(actionSetDescriptor.m_localizedName); if (!outcome.IsSuccess()) { @@ -658,10 +762,21 @@ namespace OpenXRVkBuilders ); } } + + if (actionSetDescriptor.m_actionDescriptors.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionSet [%s] must contain at least one ActionDescriptor.", + actionSetDescriptor.m_name.c_str()) + ); + } + AZStd::unordered_set uniqueActionNames; + AZStd::unordered_set uniqueActionLocalizedNames; for (const auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors) { - auto outcome = ValidateActionDescriptor(interactionProfilesAsset, actionDescriptor); + auto outcome = ValidateActionDescriptor(interactionProfilesAsset, actionDescriptor, + uniqueActionNames, uniqueActionLocalizedNames); if (!outcome.IsSuccess()) { return AZ::Failure( @@ -732,7 +847,7 @@ namespace OpenXRVkBuilders auto outcome = ValidateActionSetsAsset(*actionSetsAssetPtr.get(), *interactionProfileAssetPtr.get()); if (!outcome.IsSuccess()) { - AZ_Error(LogName, false, "Invalid source ActionSets content when using source InteractionProfiles asset file [%s]. Reason:\n[%s]", + AZ_Error(LogName, false, "Invalid source ActionSets content when using source InteractionProfiles asset file [%s]. Reason:\n%s", interactionProfileSourcePath.c_str(), outcome.GetError().c_str()); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; return; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp index fb8a3d766..81eb6521a 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp @@ -302,45 +302,4 @@ namespace OpenXRVk /// OpenXRInteractionProfilesAsset /////////////////////////////////////////////////////////// - // REMOVEME FIXME GALIB - // OpenXRInteractionProfilesAssetHandler::OpenXRInteractionProfilesAssetHandler() - // : AzFramework::GenericAssetHandler( - // OpenXRInteractionProfilesAsset::s_assetTypeName, - // "Other", - // OpenXRInteractionProfilesAsset::s_assetExtension) - // { - // } - // - // bool OpenXRInteractionProfilesAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) - // { - // auto profileAsset = asset.GetAs(); - // if (!profileAsset) - // { - // AZ_Error(LogName, false, "This should be an OpenXR Interaction Profile Asset, as this is the only type this handler can process."); - // return false; - // } - // const auto& descriptorsList = profileAsset->m_interactionProfileDescriptors; - // if (descriptorsList.empty()) - // { - // AZ_Error(LogName, false, "The list of Interaction Profile Descriptors is empty."); - // return false; - // } - // - // if (!m_serializeContext) - // { - // AZ_Error(LogName, false, "Can't save the OpenXR Interaction Profile Asset without a serialize context."); - // return false; - // } - // - // for (const auto& profileDescriptor : descriptorsList) - // { - // if (!profileDescriptor.Validate()) - // { - // return false; - // } - // } - // return AZ::Utils::SaveObjectToStream(*stream, AZ::ObjectStream::ST_JSON, profileAsset, - // asset->RTTI_GetType(), m_serializeContext); - // } - } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp index 5bf42b73d..abf1bef8c 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp @@ -91,12 +91,13 @@ namespace OpenXRVk void SystemComponent::Activate() { - //m_actionSetsAssetHandler = AZStd::make_unique>(OpenXRActionSetsAsset::s_assetTypeName, "Other", OpenXRActionSetsAsset::s_assetExtension); - //m_actionSetsAssetHandler->Register(); m_actionSetsAssetHandler = AZStd::make_unique(); m_actionSetsAssetHandler->Register(); - m_interactionProfilesAssetHandler = AZStd::make_unique(); + m_interactionProfilesAssetHandler = AZStd::make_unique>( + OpenXRInteractionProfilesAsset::s_assetTypeName, + "Other", + OpenXRInteractionProfilesAsset::s_assetExtension); m_interactionProfilesAssetHandler->Register(); if (XR::IsOpenXREnabled()) From 847209b52a017cd5ea21ae9029e07003c9e678af Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:53:12 -0600 Subject: [PATCH 25/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkActionSetsAsset.h | 2 + .../OpenXRVk/OpenXRVkAssetsValidator.h | 24 + .../OpenXRVkActionSetsAssetBuilder.cpp | 598 +---------------- .../Code/Source/OpenXRVkActionSetsAsset.cpp | 43 +- .../Code/Source/OpenXRVkAssetsValidator.cpp | 610 ++++++++++++++++++ .../Code/openxrvk_private_common_files.cmake | 2 + 6 files changed, 683 insertions(+), 596 deletions(-) create mode 100644 Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h create mode 100644 Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h index 201911f91..f1c93349c 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h @@ -117,5 +117,7 @@ namespace OpenXRVk const AZ::Data::Asset& asset, AZStd::shared_ptr stream, const AZ::Data::AssetFilterCB& assetLoadFilterCB) override; + + bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; }; }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h new file mode 100644 index 000000000..d44a131b2 --- /dev/null +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#pragma once + +#include + +#include +#include + +namespace OpenXRVkAssetsValidator +{ + AZ::Outcome ValidateInteractionProfilesAsset( + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset); + + AZ::Outcome ValidateActionSetsAsset(const OpenXRVk::OpenXRActionSetsAsset& actionSetsAsset, + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset); + +}// namespace OpenXRVkAssetsValidator diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp index 589379da9..f192040e6 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp @@ -11,10 +11,11 @@ #include #include +#include #include "OpenXRVkActionSetsAssetBuilder.h" -#pragma optimize( "", off ) // GALIB +#pragma optimize( "", off ) // GALIB FIXME REMOVE ME namespace OpenXRVkBuilders { @@ -30,47 +31,6 @@ namespace OpenXRVkBuilders return AZStd::unique_ptr(actionSetsAssetPtr); } - // A regular Name is just a utf-8 string, it can contain upper case letters - // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. - static AZ::Outcome ValidateName(const AZStd::string& name) - { - if (name.empty()) - { - return AZ::Failure("Name should not be empty."); - } - //Spaces at the beginning and end of the name are not allowed. - AZStd::string tmpName(name); - AZ::StringFunc::TrimWhiteSpace(tmpName, true, true); - if (tmpName.empty()) - { - return AZ::Failure("Name is just a bunch of spaces."); - } - if (tmpName.size() != name.size()) - { - return AZ::Failure( - AZStd::string::format("Trailing or leading spaces are not allowed in a Name [%s].", - name.c_str()) - ); - } - return AZ::Success(); - } - - - // Unlike an OpenXR Name, a Localized Name is just a utf-8 string, it can contain upper case letters - // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. - static AZ::Outcome ValidateOpenXRLocalizedName(const AZStd::string& name) - { - auto outcome = ValidateName(name); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Localized Name [%s] is invalid. Reason:\n%s", name.c_str(), outcome.GetError().c_str()) - ); - } - return AZ::Success(); - } - - void OpenXRActionSetsAssetBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { //! First get the extension @@ -132,258 +92,7 @@ namespace OpenXRVkBuilders response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } - static AZ::Outcome ValidateActionTypeString(const AZStd::string& actionTypeStr) - { - using CPD = OpenXRVk::OpenXRInteractionComponentPathDescriptor; - static const AZStd::unordered_set ValidActionTypes{ - {CPD::s_TypeBoolStr}, - {CPD::s_TypeFloatStr}, - {CPD::s_TypeVector2Str}, - {CPD::s_TypePoseStr}, - {CPD::s_TypeVibrationStr} - }; - - if (!ValidActionTypes.contains(actionTypeStr)) - { - static AZStd::string ValidListStr; - if (ValidListStr.empty()) - { - ValidListStr += "[ "; - for (const auto& validActionTypeStr : ValidActionTypes) - { - if (!ValidListStr.empty()) - { - ValidListStr += ", "; - } - ValidListStr += validActionTypeStr; - } - ValidListStr += " ]"; - } - return AZ::Failure( - AZStd::string::format("Action Type [%s] is invalid. It can only be one of %s", - actionTypeStr.c_str(), ValidListStr.c_str()) - ); - } - - return AZ::Success(); - } - - // An OpenXR path string only contain characters as described here - // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings - static AZ::Outcome ValidateOpenXRPath(const AZStd::string& path) - { - static AZStd::regex s_validCharactersRegEx(R"(^(/[a-z0-9\-_\.]+)+$)", AZStd::regex::ECMAScript); - if (!AZStd::regex_match(path, s_validCharactersRegEx)) - { - return AZ::Failure( - AZStd::string::format("The path [%s] contains an invalid character, or is missing a leading '/' or contains a leading '/'", path.c_str()) - ); - } - return AZ::Success(); - } - - static AZ::Outcome ValidateComponentPathDescriptor(const OpenXRVk::OpenXRInteractionComponentPathDescriptor& componentPathDescriptor, - AZStd::unordered_set& uniqueComponentPathNames, AZStd::unordered_set& uniqueComponentPathPaths) - { - { - if (uniqueComponentPathNames.contains(componentPathDescriptor.m_name)) - { - return AZ::Failure( - AZStd::string::format("A Component Path with name [%s] already exists.", - componentPathDescriptor.m_name.c_str()) - ); - } - uniqueComponentPathNames.emplace(componentPathDescriptor.m_name); - auto outcome = ValidateName(componentPathDescriptor.m_name); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Component Name[% s] is invalid.Reason:\n % s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - { - if (uniqueComponentPathPaths.contains(componentPathDescriptor.m_path)) - { - return AZ::Failure( - AZStd::string::format("A Component Path with path [%s] already exists.", - componentPathDescriptor.m_path.c_str()) - ); - } - uniqueComponentPathPaths.emplace(componentPathDescriptor.m_path); - auto outcome = ValidateOpenXRPath(componentPathDescriptor.m_path); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Component Path path [%s] is invalid. Reason:\n%s", componentPathDescriptor.m_path.c_str(), outcome.GetError().c_str()) - ); - } - } - - auto outcome = ValidateActionTypeString(componentPathDescriptor.m_actionTypeStr); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Component Path path [%s] has an invalid action type. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - - return AZ::Success(); - } - - - static AZ::Outcome ValidateUserPathDescriptor(const OpenXRVk::OpenXRInteractionUserPathDescriptor& userPathDescriptor, - AZStd::unordered_set& uniqueUserPathNames, AZStd::unordered_set& uniqueUserPathPaths, - AZStd::unordered_set& uniqueComponentPathNames, AZStd::unordered_set& uniqueComponentPathPaths) - { - { - if (uniqueUserPathNames.contains(userPathDescriptor.m_name)) - { - return AZ::Failure( - AZStd::string::format("An User Path with name [%s] already exists.", - userPathDescriptor.m_name.c_str()) - ); - } - uniqueUserPathNames.emplace(userPathDescriptor.m_name); - auto outcome = ValidateName(userPathDescriptor.m_name); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("User Path Name [%s] is invalid. Reason:\n%s", userPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - { - if (uniqueUserPathPaths.contains(userPathDescriptor.m_path)) - { - return AZ::Failure( - AZStd::string::format("An User Path with path [%s] already exists.", - userPathDescriptor.m_path.c_str()) - ); - } - uniqueUserPathPaths.emplace(userPathDescriptor.m_path); - auto outcome = ValidateOpenXRPath(userPathDescriptor.m_path); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("User Path path [%s] is invalid. Reason:\n%s", userPathDescriptor.m_path.c_str(), outcome.GetError().c_str()) - ); - } - } - for (const auto& componentPathDescriptor : userPathDescriptor.m_componentPathDescriptors) - { - auto outcome = ValidateComponentPathDescriptor(componentPathDescriptor, - uniqueComponentPathNames, uniqueComponentPathPaths); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Invalid Component Path [%s]. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - - return AZ::Success(); - } - - - static AZ::Outcome ValidateInteractionProfileDescriptor( - const OpenXRVk::OpenXRInteractionProfileDescriptor& interactionProfileDescriptor, - AZStd::unordered_set& uniqueNames, AZStd::unordered_set& uniquePaths) - { - { - if (uniqueNames.contains(interactionProfileDescriptor.m_uniqueName)) - { - return AZ::Failure( - AZStd::string::format("An Interaction Profile with name [%s] already exists.", - interactionProfileDescriptor.m_uniqueName.c_str()) - ); - } - uniqueNames.emplace(interactionProfileDescriptor.m_uniqueName); - auto outcome = ValidateName(interactionProfileDescriptor.m_uniqueName); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Interaction Profile Unique Name [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_uniqueName.c_str(), outcome.GetError().c_str()) - ); - } - } - - { - if (uniquePaths.contains(interactionProfileDescriptor.m_path)) - { - return AZ::Failure( - AZStd::string::format("An Interaction Profile with path [%s] already exists.", - interactionProfileDescriptor.m_path.c_str()) - ); - } - uniquePaths.emplace(interactionProfileDescriptor.m_path); - auto outcome = ValidateOpenXRPath(interactionProfileDescriptor.m_path); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Interaction Profile Path [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_path.c_str(), outcome.GetError().c_str()) - ); - } - } - - AZStd::unordered_set uniqueUserPathNames; - AZStd::unordered_set uniqueUserPathPaths; - AZStd::unordered_set uniqueComponentPathNames; - AZStd::unordered_set uniqueComponentPathPaths; - for (const auto& userPathDescriptor : interactionProfileDescriptor.m_userPathDescriptors) - { - auto outcome = ValidateUserPathDescriptor(userPathDescriptor, - uniqueUserPathNames, uniqueUserPathPaths, uniqueComponentPathNames, uniqueComponentPathPaths); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Invalid User Path [%s]. Reason:\n%s", userPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - - for (const auto& componentPathDescriptor : interactionProfileDescriptor.m_commonComponentPathDescriptors) - { - auto outcome = ValidateComponentPathDescriptor(componentPathDescriptor, - uniqueComponentPathNames, uniqueComponentPathPaths); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Invalid Common Component Path [%s]. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - - return AZ::Success(); - } - - - static AZ::Outcome ValidateInteractionProfilesAsset( - const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) - { - if (interactionProfilesAsset.m_interactionProfileDescriptors.empty()) - { - return AZ::Failure("An InteractionProfiles asset requires at least one Interaction Profile"); - } - - AZStd::unordered_set uniqueNames; - AZStd::unordered_set uniquePaths; - uint32_t i = 0; - for (const auto& interactionProfileDescriptor : interactionProfilesAsset.m_interactionProfileDescriptors) - { - auto outcome = ValidateInteractionProfileDescriptor(interactionProfileDescriptor, - uniqueNames, uniquePaths); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("InteractionProfile[%u] is invalid. Reason:\n%s", - i, outcome.GetError().c_str()) - ); - } - i++; - } - return AZ::Success(); - } + void OpenXRActionSetsAssetBuilder::ProcessInteractionProfilesAssetJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const { @@ -396,7 +105,7 @@ namespace OpenXRVkBuilders return; } - auto outcome = ValidateInteractionProfilesAsset(*interactionProfilesAssetPtr.get()); + auto outcome = OpenXRVkAssetsValidator::ValidateInteractionProfilesAsset(*interactionProfilesAssetPtr.get()); if (!outcome.IsSuccess()) { AZ_Error(LogName, false, "Invalid InteractionProfilesAsset [%s]. Reason:\n%s", @@ -459,7 +168,11 @@ namespace OpenXRVkBuilders { // Make sure the InteractionProfiles asset referenced in this ActionSets asset exists. and if so, // also declare job dependency. - auto actionSetsAssetPtr = LoadAssetAsUniquePtr(request.m_sourceFile); + constexpr bool caseInsensitive = false; + constexpr bool normalize = true; + AZStd::string sourcePath; + AZ::StringFunc::Path::Join(request.m_watchFolder.c_str(), request.m_sourceFile.c_str(), sourcePath, caseInsensitive, normalize); + auto actionSetsAssetPtr = LoadAssetAsUniquePtr(sourcePath); if (!actionSetsAssetPtr) { AZ_Error(LogName, false, "Failed to load the ActionSets asset at path[%s].", request.m_sourceFile.c_str()); @@ -501,295 +214,6 @@ namespace OpenXRVkBuilders response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } - - // An OpenXR name string only contain characters which are allowed in a SINGLE LEVEL of a well-formed path string - // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings - static AZ::Outcome ValidateOpenXRName(const AZStd::string& name) - { - static AZStd::regex s_validCharactersRegEx (R"(^[a-z0-9\-_\.]+$)", AZStd::regex::ECMAScript); - if (!AZStd::regex_match(name, s_validCharactersRegEx)) - { - return AZ::Failure( - AZStd::string::format("The name [%s] contains an invalid character", name.c_str()) - ); - } - return AZ::Success(); - } - - - static AZ::Outcome ValidateActionPathDescriptor(const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor, - const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, - AZStd::unordered_set& uniqueActionPaths) - { - auto concatenatedActionPath = actionPathDescriptor.m_interactionProfileName - + actionPathDescriptor.m_userPathName - + actionPathDescriptor.m_componentPathName; - if (uniqueActionPaths.contains(concatenatedActionPath)) - { - return AZ::Failure( - AZStd::string::format("An Action Path with profile[%s], userPath[%s], componentPath[%s] already exists.", - actionPathDescriptor.m_interactionProfileName.c_str(), - actionPathDescriptor.m_userPathName.c_str(), - actionPathDescriptor.m_componentPathName.c_str()) - ); - } - uniqueActionPaths.emplace(AZStd::move(concatenatedActionPath)); - - if (actionPathDescriptor.m_interactionProfileName.empty()) - { - return AZ::Failure( - AZStd::string::format("ActionPath Descriptor must have an InteractionProfile name.") - ); - } - const auto interactionProfileDescriptorPtr = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); - if (!interactionProfileDescriptorPtr) - { - return AZ::Failure( - AZStd::string::format("Unknown Interaction Profile Descriptor named [%s].", - actionPathDescriptor.m_interactionProfileName.c_str()) - ); - } - - if (actionPathDescriptor.m_userPathName.empty()) - { - return AZ::Failure( - AZStd::string::format("ActionPath Descriptor must have an UserPath name.") - ); - } - const auto userPathDescriptorPtr = interactionProfileDescriptorPtr->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); - if (!userPathDescriptorPtr) - { - return AZ::Failure( - AZStd::string::format("Unknown UserPath descriptor named [%s].", - actionPathDescriptor.m_userPathName.c_str()) - ); - } - - if (actionPathDescriptor.m_componentPathName.empty()) - { - return AZ::Failure( - AZStd::string::format("ActionPath Descriptor must have a ComponentPath name.") - ); - } - const auto componentPathDescriptorPtr = interactionProfileDescriptorPtr->GetComponentPathDescriptor(*userPathDescriptorPtr, actionPathDescriptor.m_componentPathName); - if (!componentPathDescriptorPtr) - { - return AZ::Failure( - AZStd::string::format("Unknown ComponentPath descriptor named [%s].", - actionPathDescriptor.m_componentPathName.c_str()) - ); - } - - return AZ::Success(); - } - - - static const AZStd::string& GetActionTypeStringFromActionPathDescriptor( - const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, - const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor - ) - { - static const AZStd::string emptyStr; - - const auto interactionProfileDescriptorPtr = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); - if (!interactionProfileDescriptorPtr) - { - return emptyStr; - } - - const auto userPathDescriptorPtr = interactionProfileDescriptorPtr->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); - if (!userPathDescriptorPtr) - { - return emptyStr; - } - const auto componentPathDescriptorPtr = interactionProfileDescriptorPtr->GetComponentPathDescriptor(*userPathDescriptorPtr, actionPathDescriptor.m_componentPathName); - if (!componentPathDescriptorPtr) - { - return emptyStr; - } - return componentPathDescriptorPtr->m_actionTypeStr; - } - - static bool IsActionTypeBoolOrFloat(const AZStd::string& actionTypeStr) - { - return ( - (actionTypeStr == OpenXRVk::OpenXRInteractionComponentPathDescriptor::s_TypeBoolStr) || - (actionTypeStr == OpenXRVk::OpenXRInteractionComponentPathDescriptor::s_TypeFloatStr) - ); - } - - static bool AreCompatibleActionTypeStrings(const AZStd::string& lhs, const AZStd::string& rhs) - { - if (IsActionTypeBoolOrFloat(lhs) && IsActionTypeBoolOrFloat(rhs)) - { - return true; - } - return (lhs == rhs); - } - - - static AZ::Outcome ValidateActionDescriptor( - const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, - const OpenXRVk::OpenXRActionDescriptor& actionDescriptor, - AZStd::unordered_set& uniqueActionNames, - AZStd::unordered_set& uniqueActionLocalizedNames) - { - { - if (uniqueActionNames.contains(actionDescriptor.m_name)) - { - return AZ::Failure( - AZStd::string::format("An Action with name [%s] already exists.", - actionDescriptor.m_name.c_str()) - ); - } - uniqueActionNames.emplace(actionDescriptor.m_name); - auto outcome = ValidateOpenXRName(actionDescriptor.m_name); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Failed to validate Action Descriptor named=[%s].\nReason:\n%s", - actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - - { - if (uniqueActionLocalizedNames.contains(actionDescriptor.m_localizedName)) - { - return AZ::Failure( - AZStd::string::format("An Action with localized name [%s] already exists.", - actionDescriptor.m_localizedName.c_str()) - ); - } - uniqueActionLocalizedNames.emplace(actionDescriptor.m_localizedName); - auto outcome = ValidateOpenXRLocalizedName(actionDescriptor.m_localizedName); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Failed to validate localized name of Action Descriptor named=[%s]\nReason:\n%s", - actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - - - if (actionDescriptor.m_actionPathDescriptors.empty()) - { - return AZ::Failure( - AZStd::string::format("At least one ActionPath Descriptor is required by Action Descriptor named=[%s].\n", - actionDescriptor.m_name.c_str()) - ); - } - - AZStd::unordered_set uniqueActionPaths; - - // It is very important that all action path descriptors have compatible data types. - const AZStd::string& firstActionTypeStr = GetActionTypeStringFromActionPathDescriptor( - interactionProfilesAsset, actionDescriptor.m_actionPathDescriptors[0]); - uint32_t actionPathIndex = 0; - for (const auto& actionPathDescriptor : actionDescriptor.m_actionPathDescriptors) - { - auto outcome = ValidateActionPathDescriptor(actionPathDescriptor, interactionProfilesAsset, uniqueActionPaths); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Failed to validate Action Path Descriptor for Action Descriptor named=[%s].\nReason:\n%s", - actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - const AZStd::string& actionTypeStr = GetActionTypeStringFromActionPathDescriptor( - interactionProfilesAsset, actionPathDescriptor); - if (!AreCompatibleActionTypeStrings(firstActionTypeStr, actionTypeStr)) - { - return AZ::Failure( - AZStd::string::format("ActionType=[%s] of ActionPath Descriptor[%u] is NOT compatible with the ActionType=[%s] ActionPath Descriptor[0]", - actionTypeStr.c_str(), actionPathIndex, firstActionTypeStr.c_str()) - ); - } - actionPathIndex++; - } - - return AZ::Success(); - } - - - static AZ::Outcome ValidateActionSetsAsset(const OpenXRVk::OpenXRActionSetsAsset& actionSetsAsset, - const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) - { - if (actionSetsAsset.m_actionSetDescriptors.empty()) - { - return AZ::Failure("At least one ActionSet must be listed in an ActionSets asset"); - } - - AZStd::unordered_set uniqueActionSetNames; - AZStd::unordered_set uniqueActionSetLocalizedNames; - for (const auto& actionSetDescriptor : actionSetsAsset.m_actionSetDescriptors) - { - { - if (uniqueActionSetNames.contains(actionSetDescriptor.m_name)) - { - return AZ::Failure( - AZStd::string::format("An ActionSet named=[%s] already exists.", - actionSetDescriptor.m_name.c_str()) - ); - } - uniqueActionSetNames.emplace(actionSetDescriptor.m_name); - auto outcome = ValidateOpenXRName(actionSetDescriptor.m_name); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", - actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - - { - if (uniqueActionSetLocalizedNames.contains(actionSetDescriptor.m_localizedName)) - { - return AZ::Failure( - AZStd::string::format("An ActionSet with localized named=[%s] already exists.", - actionSetDescriptor.m_localizedName.c_str()) - ); - } - uniqueActionSetLocalizedNames.emplace(actionSetDescriptor.m_localizedName); - auto outcome = ValidateOpenXRLocalizedName(actionSetDescriptor.m_localizedName); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", - actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - - if (actionSetDescriptor.m_actionDescriptors.empty()) - { - return AZ::Failure( - AZStd::string::format("ActionSet [%s] must contain at least one ActionDescriptor.", - actionSetDescriptor.m_name.c_str()) - ); - } - - AZStd::unordered_set uniqueActionNames; - AZStd::unordered_set uniqueActionLocalizedNames; - for (const auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors) - { - auto outcome = ValidateActionDescriptor(interactionProfilesAsset, actionDescriptor, - uniqueActionNames, uniqueActionLocalizedNames); - if (!outcome.IsSuccess()) - { - return AZ::Failure( - AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", - actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) - ); - } - } - } - - return AZ::Success(); - } - //! Each action in an actionSet has a "name" and a "localizedName". The "name" can never be empty, but //! if "localizedName" is empty we automatically patch it as an identical copy of "name". static void FixEmptyLocalizedNames(OpenXRVk::OpenXRActionSetsAsset& actionSetAsset) @@ -844,7 +268,7 @@ namespace OpenXRVkBuilders return; } - auto outcome = ValidateActionSetsAsset(*actionSetsAssetPtr.get(), *interactionProfileAssetPtr.get()); + auto outcome = OpenXRVkAssetsValidator::ValidateActionSetsAsset(*actionSetsAssetPtr.get(), *interactionProfileAssetPtr.get()); if (!outcome.IsSuccess()) { AZ_Error(LogName, false, "Invalid source ActionSets content when using source InteractionProfiles asset file [%s]. Reason:\n%s", @@ -887,4 +311,4 @@ namespace OpenXRVkBuilders } // namespace OpenXRVkBuilders -#pragma optimize( "", on ) // GALIB +#pragma optimize( "", on ) // GALIB FIXME REMOVE ME diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp index 6b4ff9b28..0adcbb79a 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace OpenXRVk { @@ -27,18 +28,18 @@ namespace OpenXRVk static AZ::Data::Asset s_asset; static constexpr char LogName[] = "EditorInternal::OpenXRInteractionProfilesAsset"; - static void BlockingReloadAssetIfNotReady() + static void BlockingReloadAssetIfNotReady(AZ::Data::Asset& profileAsset) { - if (!s_asset.GetId().IsValid()) + if (!profileAsset.GetId().IsValid()) { return; } - if (!s_asset.IsReady()) + if (!profileAsset.IsReady()) { - s_asset.QueueLoad(); - if (s_asset.IsLoading()) + profileAsset.QueueLoad(); + if (profileAsset.IsLoading()) { - s_asset.BlockUntilLoadComplete(); + profileAsset.BlockUntilLoadComplete(); } } } @@ -52,18 +53,18 @@ namespace OpenXRVk s_asset = {}; return; } - s_asset = newProfilesAsset; if (loadAsset) { - BlockingReloadAssetIfNotReady(); + BlockingReloadAssetIfNotReady(newProfilesAsset); } + s_asset = newProfilesAsset; } static const AZ::Data::Asset& GetCurrentInteractionProfilesAsset(bool loadAsset = true) { if (loadAsset) { - BlockingReloadAssetIfNotReady(); + BlockingReloadAssetIfNotReady(s_asset); } return s_asset; } @@ -365,4 +366,28 @@ namespace OpenXRVk } + bool OpenXRActionSetsAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) + { + OpenXRActionSetsAsset* assetData = asset.GetAs(); + if (!assetData->m_interactionProfilesAsset.GetId().IsValid()) + { + AZ_Error("OpenXRActionSetsAssetHandler", false, "Can't save this OpenXRActionSetsAsset without a valid OpenXRInteractionProfilesAsset") + return false; + } + + if (!assetData->m_interactionProfilesAsset.IsReady()) + { + EditorInternal::SetCurrentInteractionProfilesAsset(assetData->m_interactionProfilesAsset); + } + + auto outcome = OpenXRVkAssetsValidator::ValidateActionSetsAsset(*assetData, *(assetData->m_interactionProfilesAsset.Get())); + if (!outcome.IsSuccess()) + { + AZ_Error("OpenXRActionSetsAssetHandler", false, "Can't save this OpenXRActionSetsAsset. Reason:\n%s", outcome.GetError().c_str()); + return false; + } + + return AzFramework::GenericAssetHandler::SaveAssetData(asset, stream); + } + } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp new file mode 100644 index 000000000..c763e2317 --- /dev/null +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp @@ -0,0 +1,610 @@ +/* + * Copyright (c) Contributors to the Open 3D Engine Project. + * For complete copyright and license terms please see the LICENSE at the root of this distribution. + * + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + */ + +#include + +#include + +namespace OpenXRVkAssetsValidator +{ + /////////////////////////////////////////////////////////////////////////// + // Asset Validation Common Start + // A regular Name is just a utf-8 string, it can contain upper case letters + // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. + static AZ::Outcome ValidateName(const AZStd::string& name) + { + if (name.empty()) + { + return AZ::Failure("Name should not be empty."); + } + //Spaces at the beginning and end of the name are not allowed. + AZStd::string tmpName(name); + AZ::StringFunc::TrimWhiteSpace(tmpName, true, true); + if (tmpName.empty()) + { + return AZ::Failure("Name is just a bunch of spaces."); + } + if (tmpName.size() != name.size()) + { + return AZ::Failure( + AZStd::string::format("Trailing or leading spaces are not allowed in a Name [%s].", + name.c_str()) + ); + } + return AZ::Success(); + } + + + // Unlike an OpenXR Name, a Localized Name is just a utf-8 string, it can contain upper case letters + // and spaces in between. But it can not be empty, and can not contain leading to trailing spaces. + static AZ::Outcome ValidateOpenXRLocalizedName(const AZStd::string& name) + { + auto outcome = ValidateName(name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Localized Name [%s] is invalid. Reason:\n%s", name.c_str(), outcome.GetError().c_str()) + ); + } + return AZ::Success(); + } + + // Asset Validation Common End + /////////////////////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////////////////////// + // OpenXRInteractionProfilesAsset Validation Start + static AZ::Outcome ValidateActionTypeString(const AZStd::string& actionTypeStr) + { + using CPD = OpenXRVk::OpenXRInteractionComponentPathDescriptor; + static const AZStd::unordered_set ValidActionTypes{ + {CPD::s_TypeBoolStr}, + {CPD::s_TypeFloatStr}, + {CPD::s_TypeVector2Str}, + {CPD::s_TypePoseStr}, + {CPD::s_TypeVibrationStr} + }; + + if (!ValidActionTypes.contains(actionTypeStr)) + { + static AZStd::string ValidListStr; + if (ValidListStr.empty()) + { + ValidListStr += "[ "; + for (const auto& validActionTypeStr : ValidActionTypes) + { + if (!ValidListStr.empty()) + { + ValidListStr += ", "; + } + ValidListStr += validActionTypeStr; + } + ValidListStr += " ]"; + } + return AZ::Failure( + AZStd::string::format("Action Type [%s] is invalid. It can only be one of %s", + actionTypeStr.c_str(), ValidListStr.c_str()) + ); + } + + return AZ::Success(); + } + + // An OpenXR path string only contain characters as described here + // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings + static AZ::Outcome ValidateOpenXRPath(const AZStd::string& path) + { + static AZStd::regex s_validCharactersRegEx(R"(^(/[a-z0-9\-_\.]+)+$)", AZStd::regex::ECMAScript); + if (!AZStd::regex_match(path, s_validCharactersRegEx)) + { + return AZ::Failure( + AZStd::string::format("The path [%s] contains an invalid character, or is missing a leading '/' or contains a leading '/'", path.c_str()) + ); + } + return AZ::Success(); + } + + static AZ::Outcome ValidateComponentPathDescriptor(const OpenXRVk::OpenXRInteractionComponentPathDescriptor& componentPathDescriptor, + AZStd::unordered_set& uniqueComponentPathNames, AZStd::unordered_set& uniqueComponentPathPaths) + { + { + if (uniqueComponentPathNames.contains(componentPathDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("A Component Path with name [%s] already exists.", + componentPathDescriptor.m_name.c_str()) + ); + } + uniqueComponentPathNames.emplace(componentPathDescriptor.m_name); + auto outcome = ValidateName(componentPathDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Component Name[% s] is invalid.Reason:\n % s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + { + if (uniqueComponentPathPaths.contains(componentPathDescriptor.m_path)) + { + return AZ::Failure( + AZStd::string::format("A Component Path with path [%s] already exists.", + componentPathDescriptor.m_path.c_str()) + ); + } + uniqueComponentPathPaths.emplace(componentPathDescriptor.m_path); + auto outcome = ValidateOpenXRPath(componentPathDescriptor.m_path); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Component Path path [%s] is invalid. Reason:\n%s", componentPathDescriptor.m_path.c_str(), outcome.GetError().c_str()) + ); + } + } + + auto outcome = ValidateActionTypeString(componentPathDescriptor.m_actionTypeStr); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Component Path path [%s] has an invalid action type. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + + return AZ::Success(); + } + + + static AZ::Outcome ValidateUserPathDescriptor(const OpenXRVk::OpenXRInteractionUserPathDescriptor& userPathDescriptor, + AZStd::unordered_set& uniqueUserPathNames, AZStd::unordered_set& uniqueUserPathPaths, + AZStd::unordered_set& uniqueComponentPathNames, AZStd::unordered_set& uniqueComponentPathPaths) + { + { + if (uniqueUserPathNames.contains(userPathDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("An User Path with name [%s] already exists.", + userPathDescriptor.m_name.c_str()) + ); + } + uniqueUserPathNames.emplace(userPathDescriptor.m_name); + auto outcome = ValidateName(userPathDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("User Path Name [%s] is invalid. Reason:\n%s", userPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + { + if (uniqueUserPathPaths.contains(userPathDescriptor.m_path)) + { + return AZ::Failure( + AZStd::string::format("An User Path with path [%s] already exists.", + userPathDescriptor.m_path.c_str()) + ); + } + uniqueUserPathPaths.emplace(userPathDescriptor.m_path); + auto outcome = ValidateOpenXRPath(userPathDescriptor.m_path); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("User Path path [%s] is invalid. Reason:\n%s", userPathDescriptor.m_path.c_str(), outcome.GetError().c_str()) + ); + } + } + for (const auto& componentPathDescriptor : userPathDescriptor.m_componentPathDescriptors) + { + auto outcome = ValidateComponentPathDescriptor(componentPathDescriptor, + uniqueComponentPathNames, uniqueComponentPathPaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Invalid Component Path [%s]. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + return AZ::Success(); + } + + + static AZ::Outcome ValidateInteractionProfileDescriptor( + const OpenXRVk::OpenXRInteractionProfileDescriptor& interactionProfileDescriptor, + AZStd::unordered_set& uniqueNames, AZStd::unordered_set& uniquePaths) + { + { + if (uniqueNames.contains(interactionProfileDescriptor.m_uniqueName)) + { + return AZ::Failure( + AZStd::string::format("An Interaction Profile with name [%s] already exists.", + interactionProfileDescriptor.m_uniqueName.c_str()) + ); + } + uniqueNames.emplace(interactionProfileDescriptor.m_uniqueName); + auto outcome = ValidateName(interactionProfileDescriptor.m_uniqueName); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Interaction Profile Unique Name [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_uniqueName.c_str(), outcome.GetError().c_str()) + ); + } + } + + { + if (uniquePaths.contains(interactionProfileDescriptor.m_path)) + { + return AZ::Failure( + AZStd::string::format("An Interaction Profile with path [%s] already exists.", + interactionProfileDescriptor.m_path.c_str()) + ); + } + uniquePaths.emplace(interactionProfileDescriptor.m_path); + auto outcome = ValidateOpenXRPath(interactionProfileDescriptor.m_path); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Interaction Profile Path [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_path.c_str(), outcome.GetError().c_str()) + ); + } + } + + AZStd::unordered_set uniqueUserPathNames; + AZStd::unordered_set uniqueUserPathPaths; + AZStd::unordered_set uniqueComponentPathNames; + AZStd::unordered_set uniqueComponentPathPaths; + for (const auto& userPathDescriptor : interactionProfileDescriptor.m_userPathDescriptors) + { + auto outcome = ValidateUserPathDescriptor(userPathDescriptor, + uniqueUserPathNames, uniqueUserPathPaths, uniqueComponentPathNames, uniqueComponentPathPaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Invalid User Path [%s]. Reason:\n%s", userPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + for (const auto& componentPathDescriptor : interactionProfileDescriptor.m_commonComponentPathDescriptors) + { + auto outcome = ValidateComponentPathDescriptor(componentPathDescriptor, + uniqueComponentPathNames, uniqueComponentPathPaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Invalid Common Component Path [%s]. Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + return AZ::Success(); + } + + + AZ::Outcome ValidateInteractionProfilesAsset( + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) + { + if (interactionProfilesAsset.m_interactionProfileDescriptors.empty()) + { + return AZ::Failure("An InteractionProfiles asset requires at least one Interaction Profile"); + } + + AZStd::unordered_set uniqueNames; + AZStd::unordered_set uniquePaths; + uint32_t i = 0; + for (const auto& interactionProfileDescriptor : interactionProfilesAsset.m_interactionProfileDescriptors) + { + auto outcome = ValidateInteractionProfileDescriptor(interactionProfileDescriptor, + uniqueNames, uniquePaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("InteractionProfile[%u] is invalid. Reason:\n%s", + i, outcome.GetError().c_str()) + ); + } + i++; + } + return AZ::Success(); + } + // OpenXRInteractionProfilesAsset Validation End + /////////////////////////////////////////////////////////////////////////// + + + /////////////////////////////////////////////////////////////////////////// + // OpenXRActionSetsAsset Validation Start + static AZ::Outcome ValidateActionPathDescriptor(const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor, + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, + AZStd::unordered_set& uniqueActionPaths) + { + auto concatenatedActionPath = actionPathDescriptor.m_interactionProfileName + + actionPathDescriptor.m_userPathName + + actionPathDescriptor.m_componentPathName; + if (uniqueActionPaths.contains(concatenatedActionPath)) + { + return AZ::Failure( + AZStd::string::format("An Action Path with profile[%s], userPath[%s], componentPath[%s] already exists.", + actionPathDescriptor.m_interactionProfileName.c_str(), + actionPathDescriptor.m_userPathName.c_str(), + actionPathDescriptor.m_componentPathName.c_str()) + ); + } + uniqueActionPaths.emplace(AZStd::move(concatenatedActionPath)); + + if (actionPathDescriptor.m_interactionProfileName.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionPath Descriptor must have an InteractionProfile name.") + ); + } + const auto interactionProfileDescriptorPtr = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); + if (!interactionProfileDescriptorPtr) + { + return AZ::Failure( + AZStd::string::format("Unknown Interaction Profile Descriptor named [%s].", + actionPathDescriptor.m_interactionProfileName.c_str()) + ); + } + + if (actionPathDescriptor.m_userPathName.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionPath Descriptor must have an UserPath name.") + ); + } + const auto userPathDescriptorPtr = interactionProfileDescriptorPtr->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); + if (!userPathDescriptorPtr) + { + return AZ::Failure( + AZStd::string::format("Unknown UserPath descriptor named [%s].", + actionPathDescriptor.m_userPathName.c_str()) + ); + } + + if (actionPathDescriptor.m_componentPathName.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionPath Descriptor must have a ComponentPath name.") + ); + } + const auto componentPathDescriptorPtr = interactionProfileDescriptorPtr->GetComponentPathDescriptor(*userPathDescriptorPtr, actionPathDescriptor.m_componentPathName); + if (!componentPathDescriptorPtr) + { + return AZ::Failure( + AZStd::string::format("Unknown ComponentPath descriptor named [%s].", + actionPathDescriptor.m_componentPathName.c_str()) + ); + } + + return AZ::Success(); + } + + static const AZStd::string& GetActionTypeStringFromActionPathDescriptor( + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor + ) + { + static const AZStd::string emptyStr; + + const auto interactionProfileDescriptorPtr = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); + if (!interactionProfileDescriptorPtr) + { + return emptyStr; + } + + const auto userPathDescriptorPtr = interactionProfileDescriptorPtr->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); + if (!userPathDescriptorPtr) + { + return emptyStr; + } + const auto componentPathDescriptorPtr = interactionProfileDescriptorPtr->GetComponentPathDescriptor(*userPathDescriptorPtr, actionPathDescriptor.m_componentPathName); + if (!componentPathDescriptorPtr) + { + return emptyStr; + } + return componentPathDescriptorPtr->m_actionTypeStr; + } + + static bool IsActionTypeBoolOrFloat(const AZStd::string& actionTypeStr) + { + return ( + (actionTypeStr == OpenXRVk::OpenXRInteractionComponentPathDescriptor::s_TypeBoolStr) || + (actionTypeStr == OpenXRVk::OpenXRInteractionComponentPathDescriptor::s_TypeFloatStr) + ); + } + + static bool AreCompatibleActionTypeStrings(const AZStd::string& lhs, const AZStd::string& rhs) + { + if (IsActionTypeBoolOrFloat(lhs) && IsActionTypeBoolOrFloat(rhs)) + { + return true; + } + return (lhs == rhs); + } + + // An OpenXR name string only contain characters which are allowed in a SINGLE LEVEL of a well-formed path string + // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings + static AZ::Outcome ValidateOpenXRName(const AZStd::string& name) + { + static AZStd::regex s_validCharactersRegEx(R"(^[a-z0-9\-_\.]+$)", AZStd::regex::ECMAScript); + if (!AZStd::regex_match(name, s_validCharactersRegEx)) + { + return AZ::Failure( + AZStd::string::format("The name [%s] contains an invalid character", name.c_str()) + ); + } + return AZ::Success(); + } + + + static AZ::Outcome ValidateActionDescriptor( + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, + const OpenXRVk::OpenXRActionDescriptor& actionDescriptor, + AZStd::unordered_set& uniqueActionNames, + AZStd::unordered_set& uniqueActionLocalizedNames) + { + { + if (uniqueActionNames.contains(actionDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("An Action with name [%s] already exists.", + actionDescriptor.m_name.c_str()) + ); + } + uniqueActionNames.emplace(actionDescriptor.m_name); + auto outcome = ValidateOpenXRName(actionDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate Action Descriptor named=[%s].\nReason:\n%s", + actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + { + if (uniqueActionLocalizedNames.contains(actionDescriptor.m_localizedName)) + { + return AZ::Failure( + AZStd::string::format("An Action with localized name [%s] already exists.", + actionDescriptor.m_localizedName.c_str()) + ); + } + uniqueActionLocalizedNames.emplace(actionDescriptor.m_localizedName); + auto outcome = ValidateOpenXRLocalizedName(actionDescriptor.m_localizedName); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate localized name of Action Descriptor named=[%s]\nReason:\n%s", + actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + + if (actionDescriptor.m_actionPathDescriptors.empty()) + { + return AZ::Failure( + AZStd::string::format("At least one ActionPath Descriptor is required by Action Descriptor named=[%s].\n", + actionDescriptor.m_name.c_str()) + ); + } + + AZStd::unordered_set uniqueActionPaths; + + // It is very important that all action path descriptors have compatible data types. + const AZStd::string& firstActionTypeStr = GetActionTypeStringFromActionPathDescriptor( + interactionProfilesAsset, actionDescriptor.m_actionPathDescriptors[0]); + uint32_t actionPathIndex = 0; + for (const auto& actionPathDescriptor : actionDescriptor.m_actionPathDescriptors) + { + auto outcome = ValidateActionPathDescriptor(actionPathDescriptor, interactionProfilesAsset, uniqueActionPaths); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate Action Path Descriptor for Action Descriptor named=[%s].\nReason:\n%s", + actionDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + const AZStd::string& actionTypeStr = GetActionTypeStringFromActionPathDescriptor( + interactionProfilesAsset, actionPathDescriptor); + if (!AreCompatibleActionTypeStrings(firstActionTypeStr, actionTypeStr)) + { + return AZ::Failure( + AZStd::string::format("ActionType=[%s] of ActionPath Descriptor[%u] is NOT compatible with the ActionType=[%s] ActionPath Descriptor[0]", + actionTypeStr.c_str(), actionPathIndex, firstActionTypeStr.c_str()) + ); + } + actionPathIndex++; + } + + return AZ::Success(); + } + + + AZ::Outcome ValidateActionSetsAsset(const OpenXRVk::OpenXRActionSetsAsset& actionSetsAsset, + const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset) + { + if (actionSetsAsset.m_actionSetDescriptors.empty()) + { + return AZ::Failure("At least one ActionSet must be listed in an ActionSets asset"); + } + + AZStd::unordered_set uniqueActionSetNames; + AZStd::unordered_set uniqueActionSetLocalizedNames; + for (const auto& actionSetDescriptor : actionSetsAsset.m_actionSetDescriptors) + { + { + if (uniqueActionSetNames.contains(actionSetDescriptor.m_name)) + { + return AZ::Failure( + AZStd::string::format("An ActionSet named=[%s] already exists.", + actionSetDescriptor.m_name.c_str()) + ); + } + uniqueActionSetNames.emplace(actionSetDescriptor.m_name); + auto outcome = ValidateOpenXRName(actionSetDescriptor.m_name); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", + actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + { + if (uniqueActionSetLocalizedNames.contains(actionSetDescriptor.m_localizedName)) + { + return AZ::Failure( + AZStd::string::format("An ActionSet with localized named=[%s] already exists.", + actionSetDescriptor.m_localizedName.c_str()) + ); + } + uniqueActionSetLocalizedNames.emplace(actionSetDescriptor.m_localizedName); + auto outcome = ValidateOpenXRLocalizedName(actionSetDescriptor.m_localizedName); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", + actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + + if (actionSetDescriptor.m_actionDescriptors.empty()) + { + return AZ::Failure( + AZStd::string::format("ActionSet [%s] must contain at least one ActionDescriptor.", + actionSetDescriptor.m_name.c_str()) + ); + } + + AZStd::unordered_set uniqueActionNames; + AZStd::unordered_set uniqueActionLocalizedNames; + for (const auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors) + { + auto outcome = ValidateActionDescriptor(interactionProfilesAsset, actionDescriptor, + uniqueActionNames, uniqueActionLocalizedNames); + if (!outcome.IsSuccess()) + { + return AZ::Failure( + AZStd::string::format("Failed to validate ActionSet Descriptor name=[%s]. Reason:\n%s", + actionSetDescriptor.m_name.c_str(), outcome.GetError().c_str()) + ); + } + } + } + + return AZ::Success(); + } + // OpenXRActionSetsAsset Validation End + /////////////////////////////////////////////////////////////////////////// + + +} // namespace OpenXRVkAssetsValidator diff --git a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake index ffaa70e15..93c9c3a10 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake @@ -21,6 +21,7 @@ set(FILES Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h Include/OpenXRVk/OpenXRVkActionSetsAsset.h + Include/OpenXRVk/OpenXRVkAssetsValidator.h Source/InputDeviceXRController.cpp Source/OpenXRVkCommon.h Source/OpenXRVkDevice.cpp @@ -42,4 +43,5 @@ set(FILES Source/OpenXRVkActionsManager.h Source/OpenXRVkBehaviorReflection.cpp Source/OpenXRVkBehaviorReflection.h + Source/OpenXRVkAssetsValidator.cpp ) From fc103fb9a13d8252da27220b7912c35da38fcf1e Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:08:09 -0600 Subject: [PATCH 26/33] Added more comments. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkActionSetsAsset.h | 46 +++++++++- .../OpenXRVk/OpenXRVkActionsInterface.h | 45 +++++++--- .../OpenXRVkInteractionProfilesAsset.h | 56 +++++++----- .../OpenXRVkReferenceSpacesInterface.h | 88 ++++++++++++++----- .../Source/OpenXRVkActionSetDescriptor.cpp | 2 +- .../Code/Source/OpenXRVkActionSetsAsset.cpp | 30 +++++-- .../Code/Source/OpenXRVkActionsManager.cpp | 29 ++++-- .../Code/Source/OpenXRVkActionsManager.h | 4 - .../Code/Source/OpenXRVkAssetsValidator.cpp | 40 ++++----- .../OpenXRVkInteractionProfilesAsset.cpp | 52 ++++++----- 10 files changed, 269 insertions(+), 123 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h index f1c93349c..82f394ca3 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionSetsAsset.h @@ -14,6 +14,10 @@ namespace OpenXRVk { + //! An Action Path Descriptor is nothing more than a tuple of three + //! strings that identify a unique Input or Haptic control for a particular + //! vendor equipment. The interesting point is that these strings MUST be limited + //! to the unique names provided by an OpenXRInteractionProfileAsset. class OpenXRActionPathDescriptor final { public: @@ -24,8 +28,13 @@ namespace OpenXRVk AZStd::string GetEditorText() const; + //! Should match an OpenXRInteractionProfileDescriptor::m_name AZStd::string m_interactionProfileName; + + //! Should match an OpenXRInteractionUserPathDescriptor::m_name AZStd::string m_userPathName; + + //! Should match an OpenXRInteractionComponentPathDescriptor::m_name AZStd::string m_componentPathName; private: @@ -39,6 +48,8 @@ namespace OpenXRVk AZStd::vector GetComponentPaths() const; }; + //! Describes a custom Action I/O that will be queried/driven + //! by the application gameplay. class OpenXRActionDescriptor final { public: @@ -49,8 +60,17 @@ namespace OpenXRVk AZStd::string GetEditorText() const; + //! This name must be unique across all Actions listed in an Action Set. + //! The content of this string is limited to the characters listed here: + //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings AZStd::string m_name; // Regular char* + + //! User friendly name. AZStd::string m_localizedName; // UTF-8 string. + + //! Free form comment about this Action. + AZStd::string m_comment; + //! List of I/O action paths that will be bound to this action. //! The first action path in this list, determines what type of action paths //! can be added to the list. For example: @@ -61,6 +81,9 @@ namespace OpenXRVk AZStd::vector m_actionPathDescriptors; }; + //! Describes a custom Action Set. All applications + //! will have custom Action Sets because that's how developers define + //! the gameplay I/O. class OpenXRActionSetDescriptor final { public: @@ -71,9 +94,23 @@ namespace OpenXRVk AZStd::string GetEditorText() const; - AZStd::string m_name; // Regular char* + //! This name must be unique across all Action Sets listed in an Action Sets Asset. + //! The content of this string is limited to the characters listed here: + //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings + AZStd::string m_name; + + //! User friendly name. AZStd::string m_localizedName; // UTF-8 string. - uint32_t m_priority = 0; // Higher values mean higher priority. + + //! Higher values mean higher priority. + //! The priority is used by the OpenXR runtime in case several action sets + //! use identical action paths and the highest priority will win the event. + uint32_t m_priority = 0; + + //! Free form comment about this Action Set. + AZStd::string m_comment; + + //! List of all actions under this Action Set. AZStd::vector m_actionDescriptors; }; @@ -90,7 +127,12 @@ namespace OpenXRVk static constexpr char s_assetTypeName[] = "OpenXR Action Sets Asset"; static constexpr char s_assetExtension[] = "xractions"; + //! By referencing a particular Interaction Profiles asset, the actions + //! exposed in this Action Sets asset will be limited to the vendor support + //! profiles listed in the Interaction Profiles asset. AZ::Data::Asset m_interactionProfilesAsset; + + //! List of all Action Sets the application will work with. AZStd::vector m_actionSetDescriptors; private: diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h index 4a453bcaf..38dd98143 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkActionsInterface.h @@ -16,12 +16,23 @@ namespace OpenXRVk { + //! In addition to reading the current Pose of a particular Action + //! it is also possible to read both the linear and angular Velocity + //! of an action. When calling IOpenXRActions::GetActionStatePoseWithVelocities() + //! This is the returned data. struct PoseWithVelocities final { AZ_TYPE_INFO(PoseWithVelocities, "{AF5B9FF7-FB02-4DA4-89FB-66E605F728E2}"); static void Reflect(AZ::ReflectContext* context); + //! The current pose of the Action Space relative to the Reference Space + //! defined by IOpenXRActions::SetBaseReferenceSpaceForPoseActions(). + //! If IOpenXRActions::SetBaseReferenceSpaceForPoseActions() is never called + //! then this transform will be relative to the "View" Reference Space. AZ::Transform m_pose; + + //! To know more about how to interpret Linear and Angular Velocity see: + //! https://registry.khronos.org/OpenXR/specs/1.0/man/html/XrSpaceVelocity.html AZ::Vector3 m_linearVelocity; AZ::Vector3 m_angularVelocity; }; @@ -40,37 +51,49 @@ namespace OpenXRVk IOpenXRActions() = default; virtual ~IOpenXRActions() = default; + //! An opaque handle that will provide efficient + //! access to an Action State data. using ActionHandle = AZ::RHI::Handle; + //! Returns a list with the names of all Action Sets that are attached to the + //! current active Session. virtual AZStd::vector GetAllActionSets() const = 0; - // @returns: - // If successful: - // - true means the action set exists and is active. - // - false means the action set exists and is inactive. + //! @returns: + //! If successful: + //! - true means the action set exists and is active. + //! - false means the action set exists and is inactive. virtual AZ::Outcome GetActionSetState(const AZStd::string& actionSetName) const = 0; - // @returns: - // if successful: - // returns the current state of the action set before changing it. + + //! @returns: + //! if successful: + //! returns the current state of the action set before changing it. virtual AZ::Outcome SetActionSetState(const AZStd::string& actionSetName, bool activate) = 0; + //! @returns If an action with the given name exists, returns a successful outcome that contains + //! a handle that the caller will further utilize when calling any of the GetActionState*** methods. virtual AZ::Outcome GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) const = 0; + //! @returns A successful outcome if the action is ACTIVE and representable by the given data type. In particular, + //! if an action is of type Vector2, and it is attempted to be read with GetActionStateBoolean() then + //! most likely the returned outcome will be an error. + //! @remark When the user puts the controller/joystick down, the controllers will be automatically deactivated + //! and the Outcome won't be successful anymore. virtual AZ::Outcome GetActionStateBoolean(ActionHandle actionHandle) const = 0; virtual AZ::Outcome GetActionStateFloat(ActionHandle actionHandle) const = 0; virtual AZ::Outcome GetActionStateVector2(ActionHandle actionHandle) const = 0; //! Pose actions are also known as Action Spaces, and their Pose/Transform is always calculated //! using a reference/base visualized space. This is a stateful API that can be called at anytime. - //! See OpenXRReferenceSpacesInterface to get details about Visualized Spaces. - //! By default the base ReferenceSpace is the "View" space, which represents thhe user's head centroid. + //! See OpenXRReferenceSpacesInterface to get details about Reference Spaces. + //! By default the base Reference Space is the "View" space, which represents the user's head centroid. virtual AZ::Outcome SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName) = 0; virtual const AZStd::string& GetBaseReferenceSpaceForPoseActions() const = 0; - //! The returned transform is relative to the base Visualized Space. + //! The returned transform is relative to the base Reference Space. virtual AZ::Outcome GetActionStatePose(ActionHandle actionHandle) const = 0; - //! Same as above, but also queries location (linear) and orientation (angular) velocities + //! Same as above, but also queries Linear and Angular velocities. virtual AZ::Outcome GetActionStatePoseWithVelocities(ActionHandle actionHandle) const = 0; //! @param amplitude Will be clamped between 0.0f and 1.0f. diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h index 382788ddf..c4421d651 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h @@ -15,6 +15,9 @@ namespace OpenXRVk { + //! A Component Path Descriptor identifies Inputs or Haptics + //! available in a particular controller Like the 'X' or 'Y' Buttons + //! or the ability to vibrate (Haptic) class OpenXRInteractionComponentPathDescriptor final { public: @@ -48,6 +51,8 @@ namespace OpenXRVk }; + //! A User Path descriptor describes the XrPath (as a string) that will be + //! use to identify a Left or Right hand controller, or a Game pad controller. class OpenXRInteractionUserPathDescriptor final { public: @@ -62,8 +67,9 @@ namespace OpenXRVk //! For OpenXR a User Path string would look like: //! "/user/hand/left", or "/user/hand/right", etc AZStd::string m_path; - //! Component Paths that are only supported under this user path. - //! This list can be empty. + //! Component Paths that are only supported under this User Path. + //! This list can be empty. In case it is empty, it means that all component Paths + //! are listed under the Interaction Profile Descriptor that owns this User Path Descriptor. AZStd::vector m_componentPathDescriptors; private: @@ -71,6 +77,8 @@ namespace OpenXRVk }; + //! An Interaction Profile descriptor describes all the User Paths and Component Paths that + //! a particular Vendor Equipment supports. class OpenXRInteractionProfileDescriptor final { public: @@ -81,9 +89,6 @@ namespace OpenXRVk static constexpr char LogName[] = "OpenXRInteractionProfileDescriptor"; - //! Returns success only if the data makes sense, has proper casing, etc. - AZ::Outcome Validate() const; - const OpenXRInteractionUserPathDescriptor* GetUserPathDescriptor(const AZStd::string& userPathName) const; const OpenXRInteractionComponentPathDescriptor* GetCommonComponentPathDescriptor(const AZStd::string& componentPathName) const; const OpenXRInteractionComponentPathDescriptor* GetComponentPathDescriptor(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, const AZStd::string& componentPathName) const; @@ -92,11 +97,16 @@ namespace OpenXRVk //! Unique name across all OpenXRInteractionProfileDescriptor. //! It serves also as user friendly display name, and because //! it is unique it can be used in a dictionary. - AZStd::string m_uniqueName; + AZStd::string m_name; + //! A string convertible to XrPath like: + //! "/interaction_profiles/khr/simple_controller", or + //! "/interaction_profiles/oculus/touch_controller" AZStd::string m_path; + //! All the User Paths that this equipment supports. AZStd::vector m_userPathDescriptors; - // ComponentsPaths that are supported by all User Paths listed in @m_userPathDescriptors + + //! Common Component Paths that are supported by all User Paths listed in @m_userPathDescriptors AZStd::vector m_commonComponentPathDescriptors; private: @@ -121,25 +131,27 @@ namespace OpenXRVk static constexpr char s_assetExtension[] = "xrprofiles"; const OpenXRInteractionProfileDescriptor* GetInteractionProfileDescriptor(const AZStd::string& profileName) const; + const AZStd::string& GetActionPathTypeStr(const AZStd::string& profileName, const AZStd::string& userPathName, const AZStd::string& componentPathName) const; + //! The asset is just a list of Interaction Profile descriptors. AZStd::vector m_interactionProfileDescriptors; }; - // REMOVEME GALIB FIXME - // //! Custom asset handler - // class OpenXRInteractionProfilesAssetHandler final - // : public AzFramework::GenericAssetHandler - // { - // public: - // AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); - // AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); - // - // static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; - // - // OpenXRInteractionProfilesAssetHandler(); - // - // bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; - // }; + //! Custom asset handler that helps validate the content of the asset before allowing + //! it to be saved on disk. + class OpenXRInteractionProfilesAssetHandler final + : public AzFramework::GenericAssetHandler + { + public: + AZ_RTTI(OpenXRInteractionProfilesAssetHandler, "{1C4A27E9-6768-4C59-9582-2A01A0DEC1D0}", AzFramework::GenericAssetHandler); + AZ_CLASS_ALLOCATOR(OpenXRInteractionProfilesAssetHandler, AZ::SystemAllocator); + + static constexpr char LogName[] = "OpenXRInteractionProfilesAssetHandler"; + + OpenXRInteractionProfilesAssetHandler(); + + bool SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) override; + }; }// namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h index 7b32786ed..668898a5a 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkReferenceSpacesInterface.h @@ -18,6 +18,10 @@ namespace OpenXRVk { //! REMARK: This class must be instantiated BEFORE OpenXRActionsInterface. + //! To know more about OpenXR Reference Spaces, see the spec: + //! https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#spaces + //! This interface consolidates the API that works with definition of Reference Spaces + //! and retreival of their Poses as AZ::Transforms. class IOpenXRReferenceSpaces { public: @@ -29,7 +33,7 @@ namespace OpenXRVk // Identifiers for all reference spaces that all OpenXR implementations // must support. These are the default system spaces. Most OpenXR implementations - // support additional reference spaces, and you can try to use their id to attempt + // support additional system-provided reference spaces, and you can try to use their id to attempt // to create an space of them at runtime. This explains why this is an ordinary integral // instead of an enum. using ReferenceSpaceId = uint32_t; @@ -41,54 +45,98 @@ namespace OpenXRVk static constexpr char ReferenceSpaceNameLocal[] = "Local"; static constexpr char ReferenceSpaceNameStage[] = "Stage"; + //! @returns a list of strings, Where each string is the name of each active reference space. + //! The session will always instantiate the three reference spaces mentioned above: View, Local and Stage. + //! So this list will always contain at least three strings. virtual AZStd::vector GetReferenceSpaceNames() const = 0; - virtual AZ::Outcome AddReferenceSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) = 0; + + //! Creates a new Reference Space, and the developer will define what name they want to use to address the new space. + //! @param referenceSpaceType An integral, that identifies a system reference space, that will be used as anchor reference + //! for the new space that will be created. The caller can use other integrals, different than the three constants + //! mentioned above, but the caller would need to verify on their own that said system reference space is supported + //! by the OpenXR Runtime they are working with. OpenXR only guarantess that View, Local and Stage are always supported. + //! @param spaceName Name of the new space, and must be different than any other reference space previously created. + //! @param poseInReferenceSpace A transform that defines the default orientation and position of the new space, relative + //! to the system reference space defined by @referenceSpaceType. + //! @returns Success if the space name is unique, and the @referenceSpaceType space id is supported by the runtime. + //! In case of failure, an error description is provided. + virtual AZ::Outcome AddReferenceSpace(ReferenceSpaceId referenceSpaceType, + const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) = 0; + + //! Removes a previously user-created reference space. + //! @param spaceName The name of the reference space to remove. + //! @returns Success if a reference space with said name exists and it is NOT one of "View", "Local" or "Stage". virtual AZ::Outcome RemoveReferenceSpace(const AZStd::string& spaceName) = 0; //! REMARK: Maybe it's a good idea to move this into a private interface for the OpenXRVk Gem, //! and privately use the native handle (XrSpace) virtual const void * GetReferenceSpaceNativeHandle(const AZStd::string& spaceName) const = 0; - // Returns the Pose of @spaceName relative to @baseSpaceName. + //! Returns the Pose of @spaceName relative to @baseSpaceName. + //! This function is typically called within the game loop to poll/query the current pose + //! of a particular reference space, relative to another reference space. + //! @param spaceName The name of the reference space whose Transform the caller needs to know. + //! @param baseSpaceName The name of the base reference space that the OpenXR runtime will use to calculate + //! a relative Transform. + //! @returns If successful, a Transform that defines the pose of @spaceName, relative to @baseSpaceName. virtual AZ::Outcome GetReferenceSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) const = 0; - // By default the "View" space is the only space that will be automatically - // located on each frame. The "View" space represents the User's head centroid. - // We need a base space to use a reference when locating the user's head, and - // the default base space is the "Local" space. But you can change that with this API. - // Internally the Pose for each Eye View will be calculated relative to the View Space centroid - // and it will be reported via the notification bus: AZ::RPI::XRSpaceNotificationBus + //! With this method the caller defines the base reference space that will be used each frame + //! to locate the "View" reference space. Each frame, The "View" reference space is the only + //! reference space that will be automatically located, and its pose will be internally cached. + //! The located pose will always be relative to the @spaceName defined the last time this method + //! was called. + //! If this method is never called, the runtime will default to the "Local" reference spaces as the base reference space + //! that will be used to locate the "View" reference space. + //! This is a stateful API, you only need to call this method once, or as needed. + //! Each time GetViewSpacePose() is called, the returned Transform will be relative to @spaceName + //! @param spaceName The name of the base reference space that will be used to caculate the pose of the View space. + //! @remark Typically, the "View" reference space represents the User's head centroid. + //! Internally, the Pose for each Eye View will be calculated relative to the View Space centroid + //! and it will be reported via the notification bus: AZ::RPI::XRSpaceNotificationBus virtual AZ::Outcome SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) = 0; + + //! Returns the name of the current Reference Space that is used as base reference space + //! when locating the "View" reference space. virtual const AZStd::string& GetBaseSpaceForViewSpacePose() const = 0; - // The following poses are the same as reported by AZ::RPI::XRSpaceNotificationBus - //! Head/View pose relative to BaseSpaceForHeadCentroid. + + //! @returns The per-frame "calculated and cached" Pose of the "View" Reference Space. + //! @remark This same Pose is also reported automatically, each frame, via the AZ::RPI::XRSpaceNotificationBus. + //! Also the returned Pose is a Transform relative to the base Reference Space defined via + //! SetBaseSpaceForViewSpacePose(). virtual const AZ::Transform& GetViewSpacePose() const = 0; + //! Useful constants for OpenXR Stereo systems. static constexpr uint32_t LeftEyeView = 0; static constexpr uint32_t RightEyeView = 1; - // For an AR application running in a Phone (Mono) the count will be 1. - // For an AR/VR application running a typical headset (Stereo) the count will be 2. - // Some headsets like the Varjo support Quad Views and the eye counts will be 4. + //! For an AR application running on a Phone (Mono) the count will be 1. + //! For an AR/VR application running on a typical headset (Stereo) the count will be 2. + //! Some headsets like the Varjo support Quad Views and the eye (view) counts will be 4. virtual uint32_t GetViewCount() const = 0; - // Pose of a view(aka eye) relative to the View Space (aka Head) Pose. - // For AR applications running on a Phone (aka Mono view configuration) the Eye View Pose - // is centered exactly where the View Space Centroid is located, so calling this function wouldn't - // make much sense because it'll return an Identity transform. + //! Pose of a view(aka eye) relative to the View Space (aka Head) Pose. + //! For AR applications running on a Phone (aka Mono view configuration) the Eye View Pose + //! is centered exactly where the View Space Centroid is located, so calling this function wouldn't + //! make much sense because it'll return an Identity transform. + //! @remark DO NOT CONFUSE View Pose with "View" Space Pose. This is a confusing aspect + //! of the OpenXR API. It is tempting to call this function as GetEyePose. virtual const AZ::Transform& GetViewPose(uint32_t eyeIndex) const = 0; + //! Returns the FovData for a particular View (aka Eye). virtual const AZ::RPI::FovData& GetViewFovData(uint32_t eyeIndex) const = 0; + //! A convenient method that returns the relative pose of all Views (aka Eyes) + //! relative to the "View" Reference Space, which typically represents the user's head centroid. virtual const AZStd::vector& GetViewPoses() const = 0; - //! Forces updating the cached pose and projection data for all Views. + //! Forces updating the cached pose and projection data for all Views (Eyes). //! Each frame, all view (aka eye) poses and projections are updated automatically, making this function optional to use. //! By default the caller simply gets the per-frame cached version, which should be fine for most applications. //! This API was added because according to OpenXR xrLocateViews spec: //! "Repeatedly calling xrLocateViews with the same time may not necessarily return the same result. //! Instead the prediction gets increasingly accurate as the function is called closer to the - //! given time for which a prediction is made" + //! given time for which a prediction is made". virtual void ForceViewPosesCacheUpdate() = 0; }; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetDescriptor.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetDescriptor.cpp index d96fc005b..51d903081 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetDescriptor.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetDescriptor.cpp @@ -98,7 +98,7 @@ namespace OpenXRVk AZStd::vector profileNames; for (const auto& profileDescriptor : interactionProfilesAsset->m_interactionProfileDescriptors) { - profileNames.push_back(profileDescriptor.m_uniqueName); + profileNames.push_back(profileDescriptor.m_name); } return profileNames; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp index 0adcbb79a..0e448120d 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp @@ -125,7 +125,7 @@ namespace OpenXRVk AZStd::vector profileNames; for (const auto& profileDescriptor : interactionProfilesAsset->m_interactionProfileDescriptors) { - profileNames.push_back(profileDescriptor.m_uniqueName); + profileNames.push_back(profileDescriptor.m_name); } return profileNames; } @@ -212,6 +212,7 @@ namespace OpenXRVk ->Version(1) ->Field("Name", &OpenXRActionDescriptor::m_name) ->Field("LocalizedName", &OpenXRActionDescriptor::m_localizedName) + ->Field("Comment", &OpenXRActionDescriptor::m_comment) ->Field("ActionPathDescriptors", &OpenXRActionDescriptor::m_actionPathDescriptors) ; @@ -226,6 +227,7 @@ namespace OpenXRVk ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_localizedName, "Localized Name", "User friendly display name.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) + ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &OpenXRActionDescriptor::m_comment, "Comment", "Free form description of this Action Set.") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionDescriptor::m_actionPathDescriptors, "Action Paths", "List of action paths bound to this action") ; } @@ -234,11 +236,23 @@ namespace OpenXRVk AZStd::string OpenXRActionDescriptor::GetEditorText() const { - if (!m_localizedName.empty()) + // In addition to showing the Action name, we'll append the Action Data Type + // of the first ActionPath, so it is easy for the developer to see what will be + // the expected data type at runtime. + AZStd::string actionTypeStr = "Unknown Type"; + if (!m_actionPathDescriptors.empty()) { - return m_localizedName; + if (EditorInternal::s_asset) + { + actionTypeStr = EditorInternal::s_asset->GetActionPathTypeStr( + m_actionPathDescriptors[0].m_interactionProfileName, + m_actionPathDescriptors[0].m_userPathName, + m_actionPathDescriptors[0].m_componentPathName + ); + } } - return m_name.empty() ? "" : m_name; + AZStd::string actionDescription = AZStd::string::format("%s [%s]", m_name.empty() ? "" : m_name.c_str(), actionTypeStr.c_str()); + return actionDescription; } /// OpenXRActionDescriptor /////////////////////////////////////////////////////////// @@ -257,6 +271,7 @@ namespace OpenXRVk ->Field("Name", &OpenXRActionSetDescriptor::m_name) ->Field("LocalizedName", &OpenXRActionSetDescriptor::m_localizedName) ->Field("Priority", &OpenXRActionSetDescriptor::m_priority) + ->Field("Comment", &OpenXRActionSetDescriptor::m_comment) ->Field("ActionDescriptors", &OpenXRActionSetDescriptor::m_actionDescriptors) ; @@ -272,6 +287,7 @@ namespace OpenXRVk ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_localizedName, "Localized Name", "Action set display name.") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues")) ->DataElement(AZ::Edit::UIHandlers::SpinBox, &OpenXRActionSetDescriptor::m_priority, "Priority", "The higher this value the higher the priority.") + ->DataElement(AZ::Edit::UIHandlers::MultiLineEdit, &OpenXRActionSetDescriptor::m_comment, "Comment", "Free form description of this Action Set.") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRActionSetDescriptor::m_actionDescriptors, "Actions", "List of actions for this action set.") ; } @@ -280,11 +296,11 @@ namespace OpenXRVk AZStd::string OpenXRActionSetDescriptor::GetEditorText() const { - if (!m_localizedName.empty()) + if (!m_name.empty()) { - return m_localizedName; + return m_name; } - return m_name.empty() ? "" : m_name; + return m_localizedName.empty() ? "" : m_localizedName; } /// OpenXRActionSetDescriptor /////////////////////////////////////////////////////////// diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp index 074c73b2e..c46f3ef68 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp @@ -7,6 +7,7 @@ */ #include +#include #include @@ -62,7 +63,25 @@ namespace OpenXRVk AZStd::string ActionsManager::GetActionSetsAssetPath() { - // FIXME! Should be registry key also. + // Asset Cache relative path + static constexpr char DefaultActionsAssetPath[] = "assets/openxrvk/default.xractions"; + static constexpr char ActionSetsAssetPathRegistryKey[] = "/O3DE/Atom/OpenXR/ActionSetsAsset"; + + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + AZStd::string customPath; + bool keyExists = settingsRegistry->Get(customPath, ActionSetsAssetPathRegistryKey); + if (keyExists && !customPath.empty()) + { + AZ_Printf(LogName, "The application defined a custom ActionSets asset at path [%s].\n", + customPath.c_str()); + return customPath; + } + + } + AZ_Printf(LogName, "The application will use the default ActionSets asset at path [%s].\n", + DefaultActionsAssetPath); return AZStd::string(DefaultActionsAssetPath); } @@ -395,7 +414,7 @@ namespace OpenXRVk } // If the interaction profile is not in the dictionary, then add it. - if (!suggestedBindingsPerProfile.contains(interactionProfileDescriptor->m_uniqueName)) + if (!suggestedBindingsPerProfile.contains(interactionProfileDescriptor->m_name)) { XrPath profileXrPath; result = xrStringToPath(m_xrInstance, interactionProfileDescriptor->m_path.c_str(), &profileXrPath); @@ -405,11 +424,11 @@ namespace OpenXRVk interactionProfileDescriptor->m_path.c_str(), actionDescriptor.m_name.c_str()); continue; } - suggestedBindingsPerProfile.emplace(interactionProfileDescriptor->m_uniqueName, SuggestedBindings{}); - SuggestedBindings& newProfileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_uniqueName); + suggestedBindingsPerProfile.emplace(interactionProfileDescriptor->m_name, SuggestedBindings{}); + SuggestedBindings& newProfileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_name); newProfileBindings.m_profileXrPath = profileXrPath; } - SuggestedBindings& profileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_uniqueName); + SuggestedBindings& profileBindings = suggestedBindingsPerProfile.at(interactionProfileDescriptor->m_name); XrActionSuggestedBinding binding; binding.action = xrAction; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h index 42c24cfdd..fe6df5774 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h @@ -70,10 +70,6 @@ namespace OpenXRVk ///////////////////////////////////////////////// private: - - // Asset Cache relative path - static constexpr char DefaultActionsAssetPath[] = "openxr.xractions"; - // The following struct will be used at initialization only. // All the bindings for all actions will be called struct SuggestedBindings diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp index c763e2317..6949be0f2 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp @@ -219,19 +219,19 @@ namespace OpenXRVkAssetsValidator AZStd::unordered_set& uniqueNames, AZStd::unordered_set& uniquePaths) { { - if (uniqueNames.contains(interactionProfileDescriptor.m_uniqueName)) + if (uniqueNames.contains(interactionProfileDescriptor.m_name)) { return AZ::Failure( AZStd::string::format("An Interaction Profile with name [%s] already exists.", - interactionProfileDescriptor.m_uniqueName.c_str()) + interactionProfileDescriptor.m_name.c_str()) ); } - uniqueNames.emplace(interactionProfileDescriptor.m_uniqueName); - auto outcome = ValidateName(interactionProfileDescriptor.m_uniqueName); + uniqueNames.emplace(interactionProfileDescriptor.m_name); + auto outcome = ValidateName(interactionProfileDescriptor.m_name); if (!outcome.IsSuccess()) { return AZ::Failure( - AZStd::string::format("Interaction Profile Unique Name [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_uniqueName.c_str(), outcome.GetError().c_str()) + AZStd::string::format("Interaction Profile Unique Name [%s] is invalid. Reason:\n%s", interactionProfileDescriptor.m_name.c_str(), outcome.GetError().c_str()) ); } } @@ -389,25 +389,11 @@ namespace OpenXRVkAssetsValidator const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor ) { - static const AZStd::string emptyStr; - - const auto interactionProfileDescriptorPtr = interactionProfilesAsset.GetInteractionProfileDescriptor(actionPathDescriptor.m_interactionProfileName); - if (!interactionProfileDescriptorPtr) - { - return emptyStr; - } - - const auto userPathDescriptorPtr = interactionProfileDescriptorPtr->GetUserPathDescriptor(actionPathDescriptor.m_userPathName); - if (!userPathDescriptorPtr) - { - return emptyStr; - } - const auto componentPathDescriptorPtr = interactionProfileDescriptorPtr->GetComponentPathDescriptor(*userPathDescriptorPtr, actionPathDescriptor.m_componentPathName); - if (!componentPathDescriptorPtr) - { - return emptyStr; - } - return componentPathDescriptorPtr->m_actionTypeStr; + return interactionProfilesAsset.GetActionPathTypeStr( + actionPathDescriptor.m_interactionProfileName, + actionPathDescriptor.m_userPathName, + actionPathDescriptor.m_componentPathName + ); } static bool IsActionTypeBoolOrFloat(const AZStd::string& actionTypeStr) @@ -467,6 +453,9 @@ namespace OpenXRVkAssetsValidator } } + // Only validate if not empty. If empty, the asset builder will force this to be a copy of + // actionDescriptor.m_name. + if (!actionDescriptor.m_localizedName.empty()) { if (uniqueActionLocalizedNames.contains(actionDescriptor.m_localizedName)) { @@ -558,6 +547,9 @@ namespace OpenXRVkAssetsValidator } } + // Only validate if not empty. If empty, the asset builder will force this to be a copy of + // actionSetDescriptor.m_name. + if (!actionSetDescriptor.m_localizedName.empty()) { if (uniqueActionSetLocalizedNames.contains(actionSetDescriptor.m_localizedName)) { diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp index 81eb6521a..9df1ed1f1 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp @@ -155,7 +155,7 @@ namespace OpenXRVk { serialize->Class() ->Version(1) - ->Field("UniqueName", &OpenXRInteractionProfileDescriptor::m_uniqueName) + ->Field("UniqueName", &OpenXRInteractionProfileDescriptor::m_name) ->Field("Path", &OpenXRInteractionProfileDescriptor::m_path) ->Field("UserPathDescriptors", &OpenXRInteractionProfileDescriptor::m_userPathDescriptors) ->Field("CommonComponentPathDescriptors", &OpenXRInteractionProfileDescriptor::m_commonComponentPathDescriptors) @@ -169,7 +169,7 @@ namespace OpenXRVk ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::NameLabelOverride, &OpenXRInteractionProfileDescriptor::GetEditorText) ->Attribute(AZ::Edit::Attributes::AutoExpand, true) - ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_uniqueName, "Unique Name", "Unique name across all interaction profiles") + ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_name, "Unique Name", "Unique name across all interaction profiles") ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues) ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_path, "Path", "OpenXR Canonical Path for this interation profile.") ->DataElement(AZ::Edit::UIHandlers::Default, &OpenXRInteractionProfileDescriptor::m_userPathDescriptors, "User Paths", "List of user paths") @@ -181,32 +181,9 @@ namespace OpenXRVk AZStd::string OpenXRInteractionProfileDescriptor::GetEditorText() { - return m_uniqueName.empty() ? "" : m_uniqueName; + return m_name.empty() ? "" : m_name; } - AZ::Outcome OpenXRInteractionProfileDescriptor::Validate() const - { - if (m_uniqueName.empty()) - { - return AZ::Failure("OpenXRInteractionProfileDescriptor has no name."); - } - //Spaces at the beginning and end of the name are not allowed. - AZStd::string tmpName(m_uniqueName); - AZ::StringFunc::TrimWhiteSpace(tmpName, true, true); - if (tmpName.empty()) - { - return AZ::Failure("The OpenXRInteractionProfileDescriptor name is just a bunch of spaces."); - } - if (tmpName.size() != m_uniqueName.size()) - { - return AZ::Failure( - AZStd::string::format("Trailing or leading spaces are not allowed in the name of a OpenXRInteractionProfileDescriptor [%s].", - m_uniqueName.c_str()) - ); - } - //TODO: Validate the remaining data. - return AZ::Success(); - } const OpenXRInteractionUserPathDescriptor* OpenXRInteractionProfileDescriptor::GetUserPathDescriptor(const AZStd::string& userPathName) const { @@ -291,7 +268,7 @@ namespace OpenXRVk { for (const auto& profileDescriptor : m_interactionProfileDescriptors) { - if (profileName == profileDescriptor.m_uniqueName) + if (profileName == profileDescriptor.m_name) { return &profileDescriptor; } @@ -299,6 +276,27 @@ namespace OpenXRVk return nullptr; } + const AZStd::string& OpenXRInteractionProfilesAsset::GetActionPathTypeStr(const AZStd::string& profileName, const AZStd::string& userPathName, const AZStd::string& componentPathName) const + { + static const AZStd::string emptyStr; + const auto profileDescriptor = GetInteractionProfileDescriptor(profileName); + if (!profileDescriptor) + { + return emptyStr; + } + const auto userPathDescriptor = profileDescriptor->GetUserPathDescriptor(userPathName); + if (!userPathDescriptor) + { + return emptyStr; + } + const auto componentPathDescriptor = profileDescriptor->GetComponentPathDescriptor(*userPathDescriptor, componentPathName); + if (!componentPathDescriptor) + { + return emptyStr; + } + return componentPathDescriptor->m_actionTypeStr; + } + /// OpenXRInteractionProfilesAsset /////////////////////////////////////////////////////////// From 79bb67d7af7363ea86ef616a6f8e7b5722f551fb Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:55:55 -0600 Subject: [PATCH 27/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVkActionSetsAssetBuilder.cpp | 7 -- .../Code/Source/OpenXRVkActionsManager.cpp | 116 ++++++++---------- .../Code/Source/OpenXRVkActionsManager.h | 68 ++++++---- .../Code/Source/OpenXRVkAssetsValidator.cpp | 2 +- .../Source/OpenXRVkBehaviorReflection.cpp | 20 ++- .../Source/OpenXRVkReferenceSpacesManager.h | 4 +- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 16 +-- 7 files changed, 115 insertions(+), 118 deletions(-) diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp index f192040e6..1098367e9 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp @@ -15,8 +15,6 @@ #include "OpenXRVkActionSetsAssetBuilder.h" -#pragma optimize( "", off ) // GALIB FIXME REMOVE ME - namespace OpenXRVkBuilders { template @@ -190,10 +188,6 @@ namespace OpenXRVkBuilders for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms) { - if (platformInfo.m_identifier != "pc") //FIXME: GALIB REMOVE ME. - { - continue; - } AssetBuilderSDK::JobDescriptor jobDescriptor; // Very high priority because this asset is required to initialize the OpenXR runtime // and initialize the I/O actions system. @@ -311,4 +305,3 @@ namespace OpenXRVkBuilders } // namespace OpenXRVkBuilders -#pragma optimize( "", on ) // GALIB FIXME REMOVE ME diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp index c46f3ef68..5c89f4a85 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp @@ -64,7 +64,7 @@ namespace OpenXRVk AZStd::string ActionsManager::GetActionSetsAssetPath() { // Asset Cache relative path - static constexpr char DefaultActionsAssetPath[] = "assets/openxrvk/default.xractions"; + static constexpr char DefaultActionsAssetPath[] = "openxrvk/default.xractions"; static constexpr char ActionSetsAssetPathRegistryKey[] = "/O3DE/Atom/OpenXR/ActionSetsAsset"; auto settingsRegistry = AZ::SettingsRegistry::Get(); @@ -85,25 +85,8 @@ namespace OpenXRVk return AZStd::string(DefaultActionsAssetPath); } - bool ActionsManager::StartActionSetAssetAsync() + bool ActionsManager::LoadActionSetAssetAsync() { - // // There are two critical assets that need to be loaded. - // // The first asset defines the list of interaction profiles supported by the current version - // // of the OpenXR Gem. - // const auto interactionProfilesAssetPath = OpenXRInteractionProfilesAsset::GetInteractionProfilesAssetPath(); - // if (interactionProfilesAssetPath.empty()) - // { - // AZ_Warning(LogName, false, "No interaction profile asset has been defined. This application will run without user interaction support."); - // return true; - // } - // const auto interactionProfilesAsset = AZ::RPI::AssetUtils::LoadCriticalAsset(interactionProfilesAssetPath); - // if (!interactionProfilesAsset.IsReady()) - // { - // AZ_Warning(LogName, false, "The system interaction profiles asset [%s] is not ready. This application will run without user interaction support.", - // interactionProfilesAssetPath.c_str()); - // return true; - // } - auto productPath = GetActionSetsAssetPath(); auto assetId = AZ::RPI::AssetUtils::GetAssetIdForProductPath(productPath.c_str(), AZ::RPI::AssetUtils::TraceLevel::Error); if (!assetId.IsValid()) @@ -145,7 +128,7 @@ namespace OpenXRVk return false; } - return StartActionSetAssetAsync(); + return LoadActionSetAssetAsync(); } void ActionsManager::InitInternal() @@ -191,6 +174,7 @@ namespace OpenXRVk } if (activeProfileCount < 1) { + m_topLevelUserPaths.clear(); AZ_Error(LogName, false, "Failed to activate at least one interaction profile. This application will run without actions.\n"); return; } @@ -396,6 +380,8 @@ namespace OpenXRVk return false; } + m_topLevelUserPaths.emplace(userPathDescriptor->m_path); + const auto absoluteComponentPath = interactionProfileDescriptor->GetComponentAbsolutePath(*userPathDescriptor, actionPathDescriptor.m_componentPathName); if (absoluteComponentPath.empty()) { @@ -442,50 +428,52 @@ namespace OpenXRVk - void ActionsManager::LogCurrentInteractionProfile() + void ActionsManager::OnInteractionProfileChanged() { - // OpenXRInteractionProfileBus::EnumerateHandlers( - // [this](OpenXRInteractionProfile* handler) -> bool - // { - // auto userPathStrs = handler->GetUserPaths(); - // auto profileName = handler->GetName(); - // AZ_Printf(LogName, "Visiting user paths for interaction profile [%s]\n", profileName.c_str()); - // for (const auto& userPathStr : userPathStrs) - // { - // const auto topPathstr = handler->GetUserTopPath(userPathStr); - // XrPath xrPath; - // XrResult result = xrStringToPath(m_xrInstance, topPathstr.c_str(), &xrPath); - // if (IsError(result)) - // { - // PrintXrError(LogName, result, "Failed to get xrPath for user top path [%s]", topPathstr.c_str()); - // continue; - // } - // XrInteractionProfileState profileStateOut{ XR_TYPE_INTERACTION_PROFILE_STATE }; - // result = xrGetCurrentInteractionProfile( - // m_xrSession, xrPath, &profileStateOut); - // if (IsError(result)) - // { - // PrintXrError(LogName, result, "Failed to get profile state for user top path [%s]", topPathstr.c_str()); - // continue; - // } - // if (profileStateOut.interactionProfile == XR_NULL_PATH) - // { - // AZ_Printf(LogName, "Got an NULL Interaction Profile for [%s].\n", topPathstr.c_str()); - // continue; - // } - // AZStd::string activeProfileName = ConvertXrPathToString(m_xrInstance, profileStateOut.interactionProfile); - // if (activeProfileName.empty()) - // { - // PrintXrError(LogName, result, "Failed to convert Interaction Profile XrPath to string for user top path [%s]", topPathstr.c_str()); - // continue; - // } - // else - // { - // AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", topPathstr.c_str(), activeProfileName.c_str()); - // } - // } - // return true; - // }); + if (m_topLevelUserPaths.empty()) + { + return; + } + + uint32_t activeUserPathsCount = 0; + for (const auto& userPathStr : m_topLevelUserPaths) + { + XrPath xrUserPath; + XrResult result = xrStringToPath(m_xrInstance, userPathStr.c_str(), &xrUserPath); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to get xrPath for user top path [%s]", userPathStr.c_str()); + continue; + } + XrInteractionProfileState profileStateOut{ XR_TYPE_INTERACTION_PROFILE_STATE }; + result = xrGetCurrentInteractionProfile( + m_xrSession, xrUserPath, &profileStateOut); + if (IsError(result)) + { + PrintXrError(LogName, result, "Failed to get active profile for user top path [%s]", userPathStr.c_str()); + continue; + } + if (profileStateOut.interactionProfile == XR_NULL_PATH) + { + AZ_Printf(LogName, "Got an NULL Interaction Profile for [%s].\n", userPathStr.c_str()); + continue; + } + AZStd::string activeProfileName = ConvertXrPathToString(m_xrInstance, profileStateOut.interactionProfile); + if (activeProfileName.empty()) + { + AZ_Error(LogName, false, "Failed to convert Interaction Profile XrPath to string for user top path [%s]", userPathStr.c_str()); + continue; + } + else + { + activeUserPathsCount++; + AZ_Printf(LogName, "Current Interaction Profile for [%s] is [%s].\n", userPathStr.c_str(), activeProfileName.c_str()); + } + } + if (!activeUserPathsCount) + { + AZ_Printf(LogName, "No interaction profile is active at the moment.\n"); + } } ///////////////////////////////////////////////// @@ -548,7 +536,7 @@ namespace OpenXRVk if (itor == actionSetInfo.m_actions.end()) { return AZ::Failure( - AZStd::string::format("ActionSet with name [%s] does not have an action named [%].", + AZStd::string::format("ActionSet with name [%s] does not have an action named [%s].", actionSetName.c_str(), actionName.c_str()) ); } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h index fe6df5774..30445d0b4 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.h @@ -8,11 +8,6 @@ #pragma once -//#include -//#include -//#include -//#include - #include #include @@ -37,14 +32,18 @@ namespace OpenXRVk ActionsManager() = default; ~ActionsManager(); - //! Initialize various actions and actionSets according to the - //! "openxr.xractions" action bindings asset. + //! @returns True if it was able to start the initialization process by queueing + //! asset loading of the Action Sets asset. This is a non-blocking call + //! and the complete initialization will happend asynchronously but it + //! will be transparent to the Session. bool Init(XrInstance xrInstance, XrSession xrSession); //! Called by the Session each tick. bool SyncActions(XrTime predictedDisplayTime); - void LogCurrentInteractionProfile(); + //! Called by the Session when the event XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED is + //! received. + void OnInteractionProfileChanged(); ///////////////////////////////////////////////// /// OpenXRActionsInterface overrides @@ -82,14 +81,6 @@ namespace OpenXRVk //! The key is the user friendly profile name. using SuggestedBindingsPerProfile = AZStd::unordered_map; - //! @param actionSet The user configured data for the ActionSet. - //! @param suggestedBindingsPerProfile In this dictionary we will collect all the action bindings - //! for each interaction profile. - - //bool InitActionSetInternal(const OpenXRActionSet& actionSet, - // SuggestedBindingsPerProfile& suggestedBindingsPerProfile - // ); - struct ActionInfo { AZStd::string m_name; @@ -108,27 +99,54 @@ namespace OpenXRVk AZStd::unordered_map m_actions; }; + // Called when m_actionSetAsset is ready. + void InitInternal(); + + //! Returns the Asset Cache relative product path of the + //! ActionSets Asset. AZStd::string GetActionSetsAssetPath(); - bool StartActionSetAssetAsync(); + //! Requests the Asset System to start loading the ActionSets Asset. + //! @returns Success if the asset loading request was queued successfully. + //! @remark Once OnAssetReady event is received, the XrActionSets and XrActions + //! will be created, along with their bindings etc. + bool LoadActionSetAssetAsync(); + + //! This function is called by InitInternal() to instantiate an XrActionSet from an OpenXRActionSetDescriptor. + //! Additionally all XrActions for the XrActionSet will also be instantiated. + //! @param interactionProfilesAsset The interaction profiles asset that owns the XrPath string data + //! that will be required to instantiate XrActions. + //! @param actionSetDescriptor Contains all the data required to create an XrActionSet and all of its + //! XrActions + //! @param suggestedBindingsPerProfileOut In this dictionary, to be used later by InitInternal(), + //! we collect all the Action Bindings for each Interaction Profile referenced by the + //! OpenXRActionSetsAsset. bool InitActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, const OpenXRActionSetDescriptor& actionSetDescriptor, SuggestedBindingsPerProfile& suggestedBindingsPerProfileOut); + + //! Called by InitActionSet(...) bool AddActionToActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, ActionSetInfo& actionSetInfo, const OpenXRActionDescriptor& actionDescriptor, SuggestedBindingsPerProfile& suggestedBindingsPerProfile); + //! Called by AddActionToActionSet(...) XrAction CreateXrActionAndXrSpace(const ActionSetInfo& actionSetInfo, const OpenXRActionDescriptor& actionDescriptor, const XrActionType actionType, XrSpace& newXrActionSpace) const; + //! Called by AddActionToActionSet(...) uint32_t AppendActionBindings(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, const OpenXRActionDescriptor& actionDescriptor, XrAction xrAction, SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const; - + //! The application can selectively activate or deactivate Action Sets. + //! This is possible thanks to the public method SetActionSetState(). + //! Each time an Action Set is activated or deactivated this function is called + //! to keep @m_xrActiveActionSets up to date, which allow us to call xrSyncActions + //! with the ActionSets that should be active for the current frame. void RecreateXrActiveActionSets(); @@ -142,15 +160,12 @@ namespace OpenXRVk /// AssetBus overrides ///////////////////////////////////////////////// - // Called when m_actionSetAsset is ready. - void InitInternal(); - XrInstance m_xrInstance = XR_NULL_HANDLE; XrSession m_xrSession = XR_NULL_HANDLE; - // Loaded Asynchronously. Once this asset is ready, the real initialization of the - // OpenXR Actions and ActionSets will occur. - // This asset is loaded once and never changes. + //! Loaded Asynchronously. Once this asset is ready, the real initialization of the + //! OpenXR Actions and ActionSets will occur. + //! This asset is loaded once and never changes. AZ::Data::Asset m_actionSetAsset; // Updated each time SyncActions is called. @@ -165,6 +180,11 @@ namespace OpenXRVk //! An IOpenXRActions::ActionHandle is actually an index into this list. AZStd::vector m_actions; + //! This set is only useful for debugging purposes, we keep track + //! of all the top level user paths so we can call xrGetCurrentInteractionProfile + //! each time the Session receives the event XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED. + mutable AZStd::unordered_set m_topLevelUserPaths; + //! 32 action sets should be enough static constexpr uint32_t MaxActionSets = 32; // Each time ChangeActionSetState or ChangeActionSetsState is called diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp index 6949be0f2..717723abf 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp @@ -126,7 +126,7 @@ namespace OpenXRVkAssetsValidator if (!outcome.IsSuccess()) { return AZ::Failure( - AZStd::string::format("Component Name[% s] is invalid.Reason:\n % s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) + AZStd::string::format("Component Name[%s] is invalid.Reason:\n%s", componentPathDescriptor.m_name.c_str(), outcome.GetError().c_str()) ); } } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp index 808c11cb1..dd8bf4411 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp @@ -235,16 +235,26 @@ namespace OpenXRVk return iface->SetActionSetState(actionSetName, activate); } - static AZ::Outcome GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) + //! REMARK: The original idea was to return an: + //! AZ::Outcome + //! But when compiling for Android the Behavior Context ->Method() would statically + //! fail claiming that it was not constructible. + static IOpenXRActions::ActionHandle GetActionHandle(const AZStd::string& actionSetName, const AZStd::string& actionName) { const auto iface = OpenXRActionsInterface::Get(); if (!iface) { - return AZ::Failure( - AZStd::string::format("%s: OpenXRActionsInterface is not available.", __FUNCTION__) - ); + AZ_Error(LogName, false, "%s: OpenXRActionsInterface is not available.", __FUNCTION__); + return {}; + } + auto outcome = iface->GetActionHandle(actionSetName, actionName); + if (outcome.IsSuccess()) + { + return outcome.GetValue(); } - return iface->GetActionHandle(actionSetName, actionName); + AZ_Error(LogName, false, "%s: Failed to get action handle for actionSetName[%s], actionName[%s]. Reason:\n%s.", + __FUNCTION__, actionSetName.c_str(), actionName.c_str(), outcome.GetError().c_str()); + return {}; } static AZ::Outcome GetActionStateBoolean(IOpenXRActions::ActionHandle actionHandle) diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h index a4592c2ad..07475807d 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h @@ -23,8 +23,8 @@ namespace OpenXRVk static constexpr char LogName[] = "OpenXRVkReferenceSpacesManager"; - //! Initialize various actions and actionSets according to the - //! "openxr.xractions" action bindings asset. + //! Instantiates the OpenXR Guaranteed Reference Spaces: View, Local and Stage. + //! And prepares all the internal data required to service the OpenXRReferenceSpacesInterface interface. bool Init(XrInstance xrInstance, XrSession xrSession, XrViewConfigurationType xrViewConfigurationType, uint32_t numEyeViews); //! Called by the Session each tick. diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index 481612bfb..832999159 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -214,21 +214,7 @@ namespace OpenXRVk } case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: { - // GALIB FIXME! - AZ_Printf("GALIB", "OpenXRVkSession FIXME XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED\n"); - // m_actionsMgr->LogCurrentInteractionProfile(); - - - //if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled) - //{ - // Input* xrVkInput = GetNativeInput(); - // LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast(XR::Side::Left)), "Squeeze Left"); - // LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast(XR::Side::Right)), "Squeeze Right"); - // LogActionSourceName(xrVkInput->GetQuitAction(), "Quit"); - // LogActionSourceName(xrVkInput->GetPoseAction(static_cast(XR::Side::Left)), "Pose Left"); - // LogActionSourceName(xrVkInput->GetPoseAction(static_cast(XR::Side::Right)), "Pose Right"); - // LogActionSourceName(xrVkInput->GetVibrationAction(), "Vibrate"); - //} + m_actionsMgr->OnInteractionProfileChanged(); break; } case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: From 811ba649674c615c9b26f754dcd06ba41547f297 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Wed, 31 Jan 2024 16:53:00 -0600 Subject: [PATCH 28/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/OpenXRVkAssetsValidator.h | 8 + .../OpenXRVkActionSetsAssetBuilder.cpp | 2 +- .../Code/Source/OpenXRVkActionsManager.cpp | 2 +- .../Code/Source/OpenXRVkBehaviorReflection.h | 2 +- .../Source/OpenXRVkReferenceSpacesManager.cpp | 10 +- .../Source/OpenXRVkReferenceSpacesManager.h | 13 +- Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 7 +- .../Code/Source/XRCameraMovementComponent.cpp | 182 +++++++++--------- .../Code/Source/XRCameraMovementComponent.h | 12 +- 9 files changed, 131 insertions(+), 107 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h index d44a131b2..c77959beb 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkAssetsValidator.h @@ -13,6 +13,14 @@ #include #include +//! API that validates the content of InteractionProfiles assets and ActionSets assets. +//! In principle, this API doesn't belong in the main OpenXRVk Runtime, instead, it should be +//! private to the Asset Builders. BUT, at the moment we rely on the Asset Editor as a means to +//! provide the application developer with an UI to create these kind of assets. We need to make sure +//! the assets are valid before the user can save them from Asset Editor -> Save menu option to prevent +//! asset build failure which would cause these assets to become uneditable in the Asset Editor. +//! TODO: Develop a custom tool, either in C++ or Python to edit InteractionProfiles assets and ActionSets assets +//! and move this API to the OpenXRVk.Builders.Static target. namespace OpenXRVkAssetsValidator { AZ::Outcome ValidateInteractionProfilesAsset( diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp index 1098367e9..13baa67d1 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp @@ -294,7 +294,7 @@ namespace OpenXRVkBuilders if (!AssetBuilderSDK::OutputObject(actionSetsAssetPtr.get(), assetOutputPath, azrtti_typeid(), aznumeric_cast(0), jobProduct)) { - AZ_Error(LogName, false, "FIXME this message."); + AZ_Error(LogName, false, "Failed to output asset jobs and runtime dependencies."); response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed; return; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp index 5c89f4a85..84c49abde 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp @@ -134,7 +134,7 @@ namespace OpenXRVk void ActionsManager::InitInternal() { AZ_Assert(m_actionSetAsset.IsReady(), "Action Sets asset should be ready!"); - AZ_Assert(m_actionSetAsset->m_interactionProfilesAsset.IsReady(), "FIXME GALIB Be Readu!!"); + AZ_Assert(m_actionSetAsset->m_interactionProfilesAsset.IsReady(), "Was expecting that the Asset System would have loaded the InteractionProfiles Asset."); const auto interactionProfilesAsset = m_actionSetAsset->m_interactionProfilesAsset; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.h b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.h index fb69276ca..0a67175e3 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.h @@ -12,7 +12,7 @@ namespace OpenXRVk { /** - * Reflects OpenXR related AZ::Interfaces as classes + * Reflects OpenXR related AZ::Interfaces as static classes * in the behavior context. */ void OpenXRBehaviorReflect(AZ::BehaviorContext& context); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp index 3439fe37b..ebc75efe6 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp @@ -75,9 +75,15 @@ namespace OpenXRVk return true; } - void ReferenceSpacesManager::ResetSpaces() + void ReferenceSpacesManager::OnSessionReady() { - AZ_Error(LogName, false, "FIXME! %s", __FUNCTION__); + // REMARK: On previous versions of the OpenXRVk::Spaces API it was necessary + // to destroy and recreate the XrSpaces. But since the introduction of the + // OpenXRVk::OpenXRActionsInterface and other architectural changes + // that came with it, it appears that it is not ncessary anymore to recreate + // the XrSpaces. + // We are leaving this breadcrumb in case it becomes important in the future. + AZ_Printf(LogName, "%s. For now, this function does nothing.\n", __FUNCTION__); } const AZStd::vector& ReferenceSpacesManager::GetXrViews() const diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h index 07475807d..b0d330e92 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.h @@ -14,6 +14,10 @@ namespace OpenXRVk { + //! Concrete implementation of OpenXRReferenceSpacesInterface. + //! The OpenXRVk::Session class should own an instance of this class. + //! Automatically instantiates the default system provided reference spaces + //! as mandated by the OpenXR Spec: View, Local and Stage. class ReferenceSpacesManager final : public OpenXRReferenceSpacesInterface::Registrar { @@ -30,12 +34,14 @@ namespace OpenXRVk //! Called by the Session each tick. bool SyncViews(XrTime predictedDisplayTime); - // Spaces are reset every time the proximity sensor turns off, or the user wears the headset - // when the proximity sensor is ON. - void ResetSpaces(); + //! The Session calls this function when it receives the XR_SESSION_STATE_READY + //! event. + void OnSessionReady(); + //! This is useful for the Swapchain system. const AZStd::vector& GetXrViews() const; + //! This is useful for the Swapchain system XrSpace GetViewSpaceXrSpace() const; ///////////////////////////////////////////////// @@ -95,6 +101,7 @@ namespace OpenXRVk AZStd::vector m_eyeViewFovDatas; AZStd::vector m_xrViews; + //! Helper function to create an XrSpace. XrSpace CreateXrSpace(XrReferenceSpaceType referenceSpaceType, const AZ::Transform& relativePose); }; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index 832999159..73a37adce 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -123,12 +123,7 @@ namespace OpenXRVk XrResult result = xrBeginSession(m_session, &sessionBeginInfo); WARN_IF_UNSUCCESSFUL(result); m_sessionRunning = true; - // It's important to reset the spaces when this event is received. - // Typically the Proximity Sensor is ON, which reduces battery usage when the user - // is not wearing the headset. Each time the proximity sensor is disabled or the user - // decides to wear the headset, the XrSpaces need to be recreated, otherwise their - // poses would be corrupted. - m_referenceSpacesMgr->ResetSpaces(); + m_referenceSpacesMgr->OnSessionReady(); break; } case XR_SESSION_STATE_STOPPING: diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp index a124fd93e..9ae41501a 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp @@ -87,7 +87,7 @@ namespace OpenXRVk Camera::CameraNotificationBus::Handler::BusConnect(); if (m_isActive) { - AzFramework::InputChannelEventListener::Connect(); + //AzFramework::InputChannelEventListener::Connect(); AZ::TickBus::Handler::BusConnect(); } } @@ -98,10 +98,10 @@ namespace OpenXRVk { AZ::TickBus::Handler::BusDisconnect(); } - if (AzFramework::InputChannelEventListener::BusIsConnected()) - { - AzFramework::InputChannelEventListener::Disconnect(); - } + // if (AzFramework::InputChannelEventListener::BusIsConnected()) + // { + // AzFramework::InputChannelEventListener::Disconnect(); + // } Camera::CameraNotificationBus::Handler::BusDisconnect(); } @@ -125,43 +125,50 @@ namespace OpenXRVk AZ::TransformBus::Event(GetEntityId(), &AZ::TransformBus::Events::SetWorldTM, cameraTransform); } - bool XRCameraMovementComponent::OnInputChannelEventFiltered([[maybe_unused]] const AzFramework::InputChannel& inputChannel) - { - const auto& deviceId = inputChannel.GetInputDevice().GetInputDeviceId(); - if (AzFramework::InputDeviceXRController::IsXRControllerDevice(deviceId)) - { - OnXRControllerEvent(inputChannel); - } - return false; - } - - void XRCameraMovementComponent::OnXRControllerEvent([[maybe_unused]] const AzFramework::InputChannel& inputChannel) - { - const auto& channelId = inputChannel.GetInputChannelId(); - // This currently uses specific xr controller channels to drive the movement. Future iterations might - // use a higher-level concepts like InputMappings and InputContexts to generalize to additional - // input devices. + // void XRCameraMovementComponent::OnXRControllerEvent([[maybe_unused]] const AzFramework::InputChannel& inputChannel) + // { + // const auto& channelId = inputChannel.GetInputChannelId(); + // + // // This currently uses specific xr controller channels to drive the movement. Future iterations might + // // use a higher-level concepts like InputMappings and InputContexts to generalize to additional + // // input devices. + // + // // Left thumb-stick X/Y move the camera + // if (channelId == AzFramework::InputDeviceXRController::ThumbStickAxis1D::LX) + // { + // m_movement.SetX(inputChannel.GetValue() * m_movementSensitivity); + // } + // if (channelId == AzFramework::InputDeviceXRController::ThumbStickAxis1D::LY) + // { + // m_movement.SetY(inputChannel.GetValue() * m_movementSensitivity); + // } + // + // // A/B buttons update the height in Z of the camera + // if (channelId == AzFramework::InputDeviceXRController::Button::A) + // { // down + // m_movement.SetZ(-inputChannel.GetValue() * m_movementSensitivity); + // } + // if (channelId == AzFramework::InputDeviceXRController::Button::B) + // { // up + // m_movement.SetZ(inputChannel.GetValue() * m_movementSensitivity); + // } + // } - // Left thumb-stick X/Y move the camera - if (channelId == AzFramework::InputDeviceXRController::ThumbStickAxis1D::LX) + static float ReadActionHandleFloat(IOpenXRActions* iface, IOpenXRActions::ActionHandle actionHandle, float deadZone = 0.05f) + { + auto outcome = iface->GetActionStateFloat(actionHandle); + if (!outcome.IsSuccess()) { - m_movement.SetX(inputChannel.GetValue() * m_movementSensitivity); + // Most likely the controller went to sleep. + return 0.0f; } - if (channelId == AzFramework::InputDeviceXRController::ThumbStickAxis1D::LY) + float value = outcome.GetValue(); + if (fabsf(value) < deadZone) { - m_movement.SetY(inputChannel.GetValue() * m_movementSensitivity); - } - - // A/B buttons update the height in Z of the camera - if (channelId == AzFramework::InputDeviceXRController::Button::A) - { // down - m_movement.SetZ(-inputChannel.GetValue() * m_movementSensitivity); - } - if (channelId == AzFramework::InputDeviceXRController::Button::B) - { // up - m_movement.SetZ(inputChannel.GetValue() * m_movementSensitivity); + return 0.0f; } + return value; } void XRCameraMovementComponent::ProcessOpenXRActions() @@ -171,55 +178,50 @@ namespace OpenXRVk { return; } - // Button + + if (!m_moveFrontwaysHandle.IsValid()) { - m_movement.SetZ(0.0f); - do + // Try to cache all handles. + auto outcome = actionsIFace->GetActionHandle("main_action_set", "move_frontways"); + if (!outcome.IsSuccess()) { - auto outcomeHandle = actionsIFace->GetActionHandle("my_action_set", "button"); - if (!outcomeHandle.IsSuccess()) - { - AZ_Error("XRCameraMovementComponent", false, "%s", outcomeHandle.GetError().c_str()); - break; - } - auto actionHandle = outcomeHandle.TakeValue(); - if (!actionHandle.IsValid()) - { - break; - } - auto outcomeState = actionsIFace->GetActionStateBoolean(actionHandle); - if (!outcomeState.IsSuccess()) - { - // The action is not active, which means the controler is not being used at the moment - break; - } - if (outcomeState.GetValue()) - { - m_movement.SetZ(m_movementSensitivity); - } - } while (0); + // Most likely the Action System failed to load the ActionSets asset. + return; + } + + m_moveFrontwaysHandle = outcome.GetValue(); + AZ_Assert(m_moveFrontwaysHandle.IsValid(), "Invalid action handle"); + outcome = actionsIFace->GetActionHandle("main_action_set", "move_sideways"); + m_moveSidewaysHandle = outcome.GetValue(); + AZ_Assert(m_moveSidewaysHandle.IsValid(), "Invalid action handle"); + outcome = actionsIFace->GetActionHandle("main_action_set", "move_up"); + m_moveUpHandle = outcome.GetValue(); + AZ_Assert(m_moveUpHandle.IsValid(), "Invalid action handle"); + outcome = actionsIFace->GetActionHandle("main_action_set", "move_down"); + m_moveDownHandle = outcome.GetValue(); + AZ_Assert(m_moveDownHandle.IsValid(), "Invalid action handle"); + } + + + m_movement.Set(0.0f); + m_movement.SetY(ReadActionHandleFloat(actionsIFace, m_moveFrontwaysHandle) * m_movementSensitivity); + m_movement.SetX(ReadActionHandleFloat(actionsIFace, m_moveSidewaysHandle) * m_movementSensitivity); + + { + auto outcome = actionsIFace->GetActionStateBoolean(m_moveUpHandle); + if (outcome.IsSuccess() && outcome.GetValue()) + { + m_movement.SetZ(m_movementSensitivity); + } + } + + { + auto outcome = actionsIFace->GetActionStateBoolean(m_moveDownHandle); + if (outcome.IsSuccess() && outcome.GetValue()) + { + m_movement.SetZ(-m_movementSensitivity); + } } - // // Pose - // { - // auto actionHandle = IOpenXRActions::ActionHandle(); - // { - // auto outcome = actionsIFace->GetActionHandle("my_action_set", "left_pose"); - // if (outcome.IsSuccess()) - // { - // actionHandle = outcome.TakeValue(); - // } - // } - // if (!actionHandle.IsValid()) - // { - // return; - // } - // auto outcome = actionsIFace->GetActionStatePose(actionHandle); - // if (outcome.IsSuccess()) - // { - // AZ::Transform tm(outcome.TakeValue()); - // //AZ_Printf("Galib", "left_pose tm=\n%s\n", AZStd::to_string(tm).c_str()); - // } - // } } @@ -233,10 +235,10 @@ namespace OpenXRVk { AZ::TickBus::Handler::BusConnect(); } - if (!AzFramework::InputChannelEventListener::BusIsConnected()) - { - AzFramework::InputChannelEventListener::Connect(); - } + // if (!AzFramework::InputChannelEventListener::BusIsConnected()) + // { + // AzFramework::InputChannelEventListener::Connect(); + // } } else { @@ -244,10 +246,10 @@ namespace OpenXRVk { AZ::TickBus::Handler::BusDisconnect(); } - if (AzFramework::InputChannelEventListener::BusIsConnected()) - { - AzFramework::InputChannelEventListener::Disconnect(); - } + // if (AzFramework::InputChannelEventListener::BusIsConnected()) + // { + // AzFramework::InputChannelEventListener::Disconnect(); + // } } } diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h index 963aab5c1..e46dae5a0 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h @@ -13,6 +13,8 @@ #include #include +#include + namespace OpenXRVk { @@ -22,7 +24,6 @@ namespace OpenXRVk class XRCameraMovementComponent : public AZ::Component , public AZ::TickBus::Handler - , public AzFramework::InputChannelEventListener , public Camera::CameraNotificationBus::Handler { public: @@ -44,13 +45,13 @@ namespace OpenXRVk void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override; // AzFramework::InputChannelEventListener - bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override; + // bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override; // Camera::CameraNotificationBus::Handler overrides void OnActiveViewChanged(const AZ::EntityId&) override; private: - void OnXRControllerEvent(const AzFramework::InputChannel& inputChannel); + // void OnXRControllerEvent(const AzFramework::InputChannel& inputChannel); void ProcessOpenXRActions(); // Transient data... @@ -63,6 +64,11 @@ namespace OpenXRVk // We will process XR Actions only if the entity that owns this component is the active camera. bool m_isActive = false; + + IOpenXRActions::ActionHandle m_moveFrontwaysHandle; + IOpenXRActions::ActionHandle m_moveSidewaysHandle; + IOpenXRActions::ActionHandle m_moveUpHandle; + IOpenXRActions::ActionHandle m_moveDownHandle; }; } // namespace OpenXRVk From ceb797c55189caf4815cb4691c9133eb2327a7b0 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 1 Feb 2024 05:45:53 -0600 Subject: [PATCH 29/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../Code/Include/OpenXRVk/OpenXRVkInstance.h | 7 +++ .../OpenXRVk/Code/Source/OpenXRVkInstance.cpp | 8 ++++ Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp | 2 +- .../Code/Source/OpenXRVkSwapChain.cpp | 9 +--- .../Code/Source/XRCameraMovementComponent.cpp | 45 ------------------- .../Code/Source/XRCameraMovementComponent.h | 17 +++---- 6 files changed, 25 insertions(+), 63 deletions(-) diff --git a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h index 5dd3ee127..3d9799a3c 100644 --- a/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h +++ b/Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h @@ -65,6 +65,10 @@ namespace OpenXRVk //! Get XR configuration type. XrViewConfigurationType GetViewConfigType() const; + //! Get the number of views (aka eyes) according + //! to the current XrViewConfigurationType. + uint32_t GetViewCount() const; + protected: // XR::Instance overrides... AZ::RHI::ResultCode InitInstanceInternal() override; @@ -96,5 +100,8 @@ namespace OpenXRVk AZStd::vector m_supportedXRDevices; uint32_t m_minVulkanAPIVersion = 0; uint32_t m_maxVulkanAPIVersion = 0; + //! At runtime the number of views (eyes) will be calculate according + //! to the select view configuration in @m_viewConfigType. + uint32_t m_viewCount = 0; }; } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp index 7209afc3f..6830c9765 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp @@ -165,6 +165,9 @@ namespace OpenXRVk //TODO::Add support for other view configuration types m_viewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + result = xrEnumerateViewConfigurationViews(m_xrInstance, m_xrSystemId, m_viewConfigType, 0, &m_viewCount, nullptr); + AZ_Assert(IsSuccess(result), "Failed to read the number of views for the configuration type: %u", aznumeric_cast(m_viewConfigType)); + //TODO::Add support for other environment blend types m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; @@ -442,4 +445,9 @@ namespace OpenXRVk { return m_viewConfigType; } + + uint32_t Instance::GetViewCount() const + { + return m_viewCount; + } } diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp index 73a37adce..fab5a7260 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp @@ -56,7 +56,7 @@ namespace OpenXRVk ASSERT_IF_UNSUCCESSFUL(result); m_referenceSpacesMgr = AZStd::make_unique(); - bool success = m_referenceSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), 2 /*FIXME*/); + bool success = m_referenceSpacesMgr->Init(m_xrInstance, m_session, xrVkInstance->GetViewConfigType(), xrVkInstance->GetViewCount()); AZ_Error("OpenXRVk::Session", success, "Failed to instantiate the visualized Spaces manager."); m_actionsMgr = AZStd::make_unique(); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp index 8b50a41e3..d91982070 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp @@ -109,13 +109,8 @@ namespace OpenXRVk systemProperties.trackingProperties.positionTracking == XR_TRUE ? "True" : "False"); } - //Only supporting XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO for now - XrViewConfigurationType viewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; - - result = xrEnumerateViewConfigurationViews(xrInstance, xrSystemId, - viewConfigType, 0, &m_numViews, nullptr); - WARN_IF_UNSUCCESSFUL(result); - + m_numViews = xrVkInstance->GetViewCount(); + XrViewConfigurationType viewConfigType = xrVkInstance->GetViewConfigType(); m_configViews.resize(m_numViews, { XR_TYPE_VIEW_CONFIGURATION_VIEW }); result = xrEnumerateViewConfigurationViews(xrInstance, xrSystemId, viewConfigType, m_numViews, &m_numViews, m_configViews.data()); diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp index 9ae41501a..6f7d56725 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp @@ -13,9 +13,6 @@ #include #include -#include // GALIB - -#include #include #include @@ -87,7 +84,6 @@ namespace OpenXRVk Camera::CameraNotificationBus::Handler::BusConnect(); if (m_isActive) { - //AzFramework::InputChannelEventListener::Connect(); AZ::TickBus::Handler::BusConnect(); } } @@ -98,10 +94,6 @@ namespace OpenXRVk { AZ::TickBus::Handler::BusDisconnect(); } - // if (AzFramework::InputChannelEventListener::BusIsConnected()) - // { - // AzFramework::InputChannelEventListener::Disconnect(); - // } Camera::CameraNotificationBus::Handler::BusDisconnect(); } @@ -126,35 +118,6 @@ namespace OpenXRVk } - // void XRCameraMovementComponent::OnXRControllerEvent([[maybe_unused]] const AzFramework::InputChannel& inputChannel) - // { - // const auto& channelId = inputChannel.GetInputChannelId(); - // - // // This currently uses specific xr controller channels to drive the movement. Future iterations might - // // use a higher-level concepts like InputMappings and InputContexts to generalize to additional - // // input devices. - // - // // Left thumb-stick X/Y move the camera - // if (channelId == AzFramework::InputDeviceXRController::ThumbStickAxis1D::LX) - // { - // m_movement.SetX(inputChannel.GetValue() * m_movementSensitivity); - // } - // if (channelId == AzFramework::InputDeviceXRController::ThumbStickAxis1D::LY) - // { - // m_movement.SetY(inputChannel.GetValue() * m_movementSensitivity); - // } - // - // // A/B buttons update the height in Z of the camera - // if (channelId == AzFramework::InputDeviceXRController::Button::A) - // { // down - // m_movement.SetZ(-inputChannel.GetValue() * m_movementSensitivity); - // } - // if (channelId == AzFramework::InputDeviceXRController::Button::B) - // { // up - // m_movement.SetZ(inputChannel.GetValue() * m_movementSensitivity); - // } - // } - static float ReadActionHandleFloat(IOpenXRActions* iface, IOpenXRActions::ActionHandle actionHandle, float deadZone = 0.05f) { auto outcome = iface->GetActionStateFloat(actionHandle); @@ -235,10 +198,6 @@ namespace OpenXRVk { AZ::TickBus::Handler::BusConnect(); } - // if (!AzFramework::InputChannelEventListener::BusIsConnected()) - // { - // AzFramework::InputChannelEventListener::Connect(); - // } } else { @@ -246,10 +205,6 @@ namespace OpenXRVk { AZ::TickBus::Handler::BusDisconnect(); } - // if (AzFramework::InputChannelEventListener::BusIsConnected()) - // { - // AzFramework::InputChannelEventListener::Disconnect(); - // } } } diff --git a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h index e46dae5a0..37f735b63 100644 --- a/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h +++ b/Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -18,7 +17,7 @@ namespace OpenXRVk { - //! XRCameraMovementComponent integrates XR Controller inputs to control a camera. + //! XRCameraMovementComponent uses the OpenXRVk::OpenXRActionsInterface to read user input to control a camera. //! This is an example that hooks up a limited set of inputs, mostly thumbsticks, to //! drive the camera position to new places. class XRCameraMovementComponent @@ -44,14 +43,10 @@ namespace OpenXRVk // AZ::TickBus::Handler void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override; - // AzFramework::InputChannelEventListener - // bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override; - // Camera::CameraNotificationBus::Handler overrides void OnActiveViewChanged(const AZ::EntityId&) override; private: - // void OnXRControllerEvent(const AzFramework::InputChannel& inputChannel); void ProcessOpenXRActions(); // Transient data... @@ -65,10 +60,12 @@ namespace OpenXRVk // We will process XR Actions only if the entity that owns this component is the active camera. bool m_isActive = false; - IOpenXRActions::ActionHandle m_moveFrontwaysHandle; - IOpenXRActions::ActionHandle m_moveSidewaysHandle; - IOpenXRActions::ActionHandle m_moveUpHandle; - IOpenXRActions::ActionHandle m_moveDownHandle; + //! A cache of OpenXRVk Action Handles that provide straight + //! access into the user's input. + IOpenXRActions::ActionHandle m_moveFrontwaysHandle; // Float + IOpenXRActions::ActionHandle m_moveSidewaysHandle; // Float + IOpenXRActions::ActionHandle m_moveUpHandle; // Boolean + IOpenXRActions::ActionHandle m_moveDownHandle; // Boolean }; } // namespace OpenXRVk From 3f275d067808a68810fc1c27625650eb433f3f85 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:11:13 -0600 Subject: [PATCH 30/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- Gems/OpenXRVk/Assets/OpenXRVk/README.md | 25 +++ .../Assets/OpenXRVk/default.xractions | 101 +++++++++++ .../Assets/OpenXRVk/system.xrprofiles | 171 ++++++++++++++++++ ...tBuilder.cpp => OpenXRVkAssetsBuilder.cpp} | 18 +- ...AssetBuilder.h => OpenXRVkAssetsBuilder.h} | 12 +- ... OpenXRVkAssetsBuilderSystemComponent.cpp} | 26 +-- ...=> OpenXRVkAssetsBuilderSystemComponent.h} | 8 +- .../Source/Builders/OpenXRVkBuilderModule.cpp | 4 +- .../OpenXRVk/Code/Source/OpenXRVkInstance.cpp | 9 +- .../Code/openxrvk_private_builder_files.cmake | 8 +- 10 files changed, 342 insertions(+), 40 deletions(-) create mode 100644 Gems/OpenXRVk/Assets/OpenXRVk/README.md create mode 100644 Gems/OpenXRVk/Assets/OpenXRVk/default.xractions create mode 100644 Gems/OpenXRVk/Assets/OpenXRVk/system.xrprofiles rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRVkActionSetsAssetBuilder.cpp => OpenXRVkAssetsBuilder.cpp} (91%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRVkActionSetsAssetBuilder.h => OpenXRVkAssetsBuilder.h} (81%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRVkAssetBuildersSystemComponent.cpp => OpenXRVkAssetsBuilderSystemComponent.cpp} (73%) rename Gems/OpenXRVk/Code/Source/Builders/{OpenXRVkAssetBuildersSystemComponent.h => OpenXRVkAssetsBuilderSystemComponent.h} (87%) diff --git a/Gems/OpenXRVk/Assets/OpenXRVk/README.md b/Gems/OpenXRVk/Assets/OpenXRVk/README.md new file mode 100644 index 000000000..e169a643b --- /dev/null +++ b/Gems/OpenXRVk/Assets/OpenXRVk/README.md @@ -0,0 +1,25 @@ +# About system.xrprofiles and default.xractions + +Both of these assets are editable via the `Asset Editor` UI. The `Asset Editor` is a tool provided by the O3DE Editor. + +`system.xrprofiles` defines a list of standard OpenXR interaction profiles that have been tested with O3DE. +For more information read the header file: `.../Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInteractionProfilesAsset.h`. + +`default.xraction` depends on `system.xrprofiles` and it defines a set of actions that your application can use +to read user input or drive haptic feedback signals. This is the default asset that the OpenXRVk Gem will load, but can be overriden with help of the following Registry Key: +**"/O3DE/Atom/OpenXR/ActionSetsAsset"**. If this key is not defined, the application will default to: **"openxrvk/default.xractions"**. + +Here is an example of an application named `AdventureVR` that customizes the Action Sets asset: +- ActionSet Asset Location: \/Assets/AdventureVR/adventurevr.xractions +- Registry File Location: \/Registry/adventurevr.setreg, with the following content: +```json +{ + "O3DE": { + "Atom": { + "OpenXR": { + "ActionSetsAsset": "adventurevr/adventurevr.xractions" + } + } + } +} +``` diff --git a/Gems/OpenXRVk/Assets/OpenXRVk/default.xractions b/Gems/OpenXRVk/Assets/OpenXRVk/default.xractions new file mode 100644 index 000000000..3bf7c96c4 --- /dev/null +++ b/Gems/OpenXRVk/Assets/OpenXRVk/default.xractions @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/OpenXRVk/Assets/OpenXRVk/system.xrprofiles b/Gems/OpenXRVk/Assets/OpenXRVk/system.xrprofiles new file mode 100644 index 000000000..308c9eb3c --- /dev/null +++ b/Gems/OpenXRVk/Assets/OpenXRVk/system.xrprofiles @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp similarity index 91% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp index 13baa67d1..f97fe6fba 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp @@ -13,7 +13,7 @@ #include #include -#include "OpenXRVkActionSetsAssetBuilder.h" +#include "OpenXRVkAssetsBuilder.h" namespace OpenXRVkBuilders { @@ -29,7 +29,7 @@ namespace OpenXRVkBuilders return AZStd::unique_ptr(actionSetsAssetPtr); } - void OpenXRActionSetsAssetBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const + void OpenXRAssetsBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { //! First get the extension constexpr bool includeDot = false; @@ -53,7 +53,7 @@ namespace OpenXRVkBuilders } - void OpenXRActionSetsAssetBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const + void OpenXRAssetsBuilder::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const { //! First get the extension constexpr bool includeDot = false; @@ -73,7 +73,7 @@ namespace OpenXRVkBuilders ///////////////////////////////////////////////////////////////////////////////// // OpenXRInteractionProfilesAsset Support Begin - void OpenXRActionSetsAssetBuilder::CreateInteractionProfilesAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const + void OpenXRAssetsBuilder::CreateInteractionProfilesAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms) { @@ -92,7 +92,7 @@ namespace OpenXRVkBuilders - void OpenXRActionSetsAssetBuilder::ProcessInteractionProfilesAssetJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const + void OpenXRAssetsBuilder::ProcessInteractionProfilesAssetJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const { // Open the file, and make sure there's no redundant data, the OpenXR Paths are well formatted, etc. auto interactionProfilesAssetPtr = LoadAssetAsUniquePtr(request.m_fullPath); @@ -162,7 +162,7 @@ namespace OpenXRVkBuilders return sourcePath; } - void OpenXRActionSetsAssetBuilder::CreateActionSetsAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const + void OpenXRAssetsBuilder::CreateActionSetsAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { // Make sure the InteractionProfiles asset referenced in this ActionSets asset exists. and if so, // also declare job dependency. @@ -216,14 +216,14 @@ namespace OpenXRVkBuilders { if (actionSetDescriptor.m_localizedName.empty()) { - AZ_Printf(OpenXRActionSetsAssetBuilder::LogName, "ActionSet had empty LocalizedName. Taking new value of [%s]", actionSetDescriptor.m_name.c_str()); + AZ_Printf(OpenXRAssetsBuilder::LogName, "ActionSet had empty LocalizedName. Taking new value of [%s]", actionSetDescriptor.m_name.c_str()); actionSetDescriptor.m_localizedName = actionSetDescriptor.m_name; } for (auto& actionDescriptor : actionSetDescriptor.m_actionDescriptors) { if (actionDescriptor.m_localizedName.empty()) { - AZ_Printf(OpenXRActionSetsAssetBuilder::LogName, "Action in ActionSet [%s] had empty LocalizedName. Taking new value of [%s]", + AZ_Printf(OpenXRAssetsBuilder::LogName, "Action in ActionSet [%s] had empty LocalizedName. Taking new value of [%s]", actionSetDescriptor.m_name.c_str(), actionDescriptor.m_name.c_str()); actionDescriptor.m_localizedName = actionDescriptor.m_name; } @@ -231,7 +231,7 @@ namespace OpenXRVkBuilders } } - void OpenXRActionSetsAssetBuilder::ProcessActionSetsAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const + void OpenXRAssetsBuilder::ProcessActionSetsAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const { auto actionSetsAssetPtr = LoadAssetAsUniquePtr(request.m_fullPath); if (!actionSetsAssetPtr) diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.h similarity index 81% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.h index 55ceef4bd..38e66c44a 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkActionSetsAssetBuilder.h +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.h @@ -13,19 +13,19 @@ namespace OpenXRVkBuilders { - class OpenXRActionSetsAssetBuilder final + class OpenXRAssetsBuilder final : public AssetBuilderSDK::AssetBuilderCommandBus::Handler { public: - AZ_TYPE_INFO(OpenXRActionSetsAssetBuilder, "{1D053000-7799-459D-B99B-FF6AE6394BC1}"); + AZ_TYPE_INFO(OpenXRAssetsBuilder, "{1D053000-7799-459D-B99B-FF6AE6394BC1}"); - static constexpr char LogName[] = "OpenXRActionSetsAssetBuilder"; + static constexpr char LogName[] = "OpenXRAssetsBuilder"; static constexpr const char* InteractionProfilesAssetJobKey = "XR Interaction Profiles Asset"; static constexpr const char* ActionSetsAssetJobKey = "XR Action Sets Asset"; - OpenXRActionSetsAssetBuilder() = default; - ~OpenXRActionSetsAssetBuilder() = default; + OpenXRAssetsBuilder() = default; + ~OpenXRAssetsBuilder() = default; // Asset Builder Callback Functions void CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; @@ -35,7 +35,7 @@ namespace OpenXRVkBuilders void ShutDown() override { }; private: - AZ_DISABLE_COPY_MOVE(OpenXRActionSetsAssetBuilder); + AZ_DISABLE_COPY_MOVE(OpenXRAssetsBuilder); void CreateInteractionProfilesAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const; void ProcessInteractionProfilesAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const; diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilderSystemComponent.cpp similarity index 73% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilderSystemComponent.cpp index 19d541b62..a1ec35055 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilderSystemComponent.cpp @@ -12,49 +12,49 @@ #include #include -#include "OpenXRVkAssetBuildersSystemComponent.h" +#include "OpenXRVkAssetsBuilderSystemComponent.h" namespace OpenXRVkBuilders { - void OpenXRAssetBuildersSystemComponent::Reflect(AZ::ReflectContext* context) + void OpenXRAssetsBuilderSystemComponent::Reflect(AZ::ReflectContext* context) { OpenXRVk::OpenXRInteractionProfilesAsset::Reflect(context); OpenXRVk::OpenXRActionSetsAsset::Reflect(context); if (AZ::SerializeContext* serialize = azrtti_cast(context)) { - serialize->Class() + serialize->Class() ->Version(1) ->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector({ AssetBuilderSDK::ComponentTags::AssetBuilder })) ; } } - void OpenXRAssetBuildersSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + void OpenXRAssetsBuilderSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) { provided.push_back(AZ_CRC("OpenXRAssetsBuilderService")); } - void OpenXRAssetBuildersSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + void OpenXRAssetsBuilderSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) { incompatible.push_back(AZ_CRC("OpenXRAssetsBuilderService")); } - void OpenXRAssetBuildersSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + void OpenXRAssetsBuilderSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) { (void)required; } - void OpenXRAssetBuildersSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + void OpenXRAssetsBuilderSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) { dependent.push_back(AZ_CRC("AssetCatalogService")); } - void OpenXRAssetBuildersSystemComponent::Init() + void OpenXRAssetsBuilderSystemComponent::Init() { } - void OpenXRAssetBuildersSystemComponent::Activate() + void OpenXRAssetsBuilderSystemComponent::Activate() { // Register Shader Asset Builder AssetBuilderSDK::AssetBuilderDesc assetBuilderDescriptor; @@ -62,15 +62,15 @@ namespace OpenXRVkBuilders assetBuilderDescriptor.m_version = 1; // First versuib assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRVk::OpenXRInteractionProfilesAsset::s_assetExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); assetBuilderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(AZStd::string::format("*.%s", OpenXRVk::OpenXRActionSetsAsset::s_assetExtension), AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard)); - assetBuilderDescriptor.m_busId = azrtti_typeid(); - assetBuilderDescriptor.m_createJobFunction = AZStd::bind(&OpenXRActionSetsAssetBuilder::CreateJobs, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); - assetBuilderDescriptor.m_processJobFunction = AZStd::bind(&OpenXRActionSetsAssetBuilder::ProcessJob, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + assetBuilderDescriptor.m_busId = azrtti_typeid(); + assetBuilderDescriptor.m_createJobFunction = AZStd::bind(&OpenXRAssetsBuilder::CreateJobs, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); + assetBuilderDescriptor.m_processJobFunction = AZStd::bind(&OpenXRAssetsBuilder::ProcessJob, &m_actionSetsAssetBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2); m_actionSetsAssetBuilder.BusConnect(assetBuilderDescriptor.m_busId); AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBus::Handler::RegisterBuilderInformation, assetBuilderDescriptor); } - void OpenXRAssetBuildersSystemComponent::Deactivate() + void OpenXRAssetsBuilderSystemComponent::Deactivate() { m_actionSetsAssetBuilder.BusDisconnect(); } diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilderSystemComponent.h similarity index 87% rename from Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h rename to Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilderSystemComponent.h index 02b55fcd6..ba3a9758c 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetBuildersSystemComponent.h +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilderSystemComponent.h @@ -10,15 +10,15 @@ #include -#include "OpenXRVkActionSetsAssetBuilder.h" +#include "OpenXRVkAssetsBuilder.h" namespace OpenXRVkBuilders { - class OpenXRAssetBuildersSystemComponent + class OpenXRAssetsBuilderSystemComponent : public AZ::Component { public: - AZ_COMPONENT(OpenXRAssetBuildersSystemComponent, "{B046B553-CAB4-4AE5-9192-5E002771979B}"); + AZ_COMPONENT(OpenXRAssetsBuilderSystemComponent, "{B046B553-CAB4-4AE5-9192-5E002771979B}"); static void Reflect(AZ::ReflectContext* context); @@ -42,7 +42,7 @@ namespace OpenXRVkBuilders private: - OpenXRActionSetsAssetBuilder m_actionSetsAssetBuilder; + OpenXRAssetsBuilder m_actionSetsAssetBuilder; }; diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp index b78ef12d1..8e08faadc 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp @@ -24,7 +24,7 @@ namespace OpenXRVkBuilders { // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. m_descriptors.insert(m_descriptors.end(), { - OpenXRAssetBuildersSystemComponent::CreateDescriptor(), + OpenXRAssetsBuilderSystemComponent::CreateDescriptor(), }); } @@ -34,7 +34,7 @@ namespace OpenXRVkBuilders AZ::ComponentTypeList GetRequiredSystemComponents() const override { return AZ::ComponentTypeList{ - azrtti_typeid(), + azrtti_typeid(), }; } }; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp index 6830c9765..4433da572 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp @@ -165,9 +165,14 @@ namespace OpenXRVk //TODO::Add support for other view configuration types m_viewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + m_viewCount = 0; result = xrEnumerateViewConfigurationViews(m_xrInstance, m_xrSystemId, m_viewConfigType, 0, &m_viewCount, nullptr); - AZ_Assert(IsSuccess(result), "Failed to read the number of views for the configuration type: %u", aznumeric_cast(m_viewConfigType)); - + if (IsError(result)) + { + PrintXrError("OpenXRVk", result, "Failed to read the number of views for the configuration type: %u.", aznumeric_cast(m_viewConfigType)); + return AZ::RHI::ResultCode::Fail; + } + AZ_Assert(m_viewCount > 0, "View count should be greater than 0."); //TODO::Add support for other environment blend types m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; diff --git a/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake b/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake index b26c1ab73..1744b632c 100644 --- a/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake +++ b/Gems/OpenXRVk/Code/openxrvk_private_builder_files.cmake @@ -7,8 +7,8 @@ # set(FILES - Source/Builders/OpenXRVkActionSetsAssetBuilder.cpp - Source/Builders/OpenXRVkActionSetsAssetBuilder.h - Source/Builders/OpenXRVkAssetBuildersSystemComponent.cpp - Source/Builders/OpenXRVkAssetBuildersSystemComponent.h + Source/Builders/OpenXRVkAssetsBuilder.cpp + Source/Builders/OpenXRVkAssetsBuilder.h + Source/Builders/OpenXRVkAssetsBuilderSystemComponent.cpp + Source/Builders/OpenXRVkAssetsBuilderSystemComponent.h ) From e04ea364cea396122dbb4a66e2f2a054ae1b8a2c Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:30:19 -0600 Subject: [PATCH 31/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- .../OpenXRVk/Scripts/xr_camera_move.lua | 182 ++++++++++++++++++ .../OpenXRVk/Scripts/xr_spaces_api_test.lua | 85 ++++++++ .../Code/Source/OpenXRVkActionSetsAsset.cpp | 33 +++- .../Code/Source/OpenXRVkActionsManager.cpp | 21 +- .../Code/Source/OpenXRVkAssetsValidator.cpp | 6 + .../Source/OpenXRVkBehaviorReflection.cpp | 25 +++ .../OpenXRVkInteractionProfilesAsset.cpp | 12 ++ .../Source/OpenXRVkReferenceSpacesManager.cpp | 15 ++ 8 files changed, 375 insertions(+), 4 deletions(-) create mode 100644 Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_camera_move.lua create mode 100644 Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_spaces_api_test.lua diff --git a/Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_camera_move.lua b/Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_camera_move.lua new file mode 100644 index 000000000..46bcc2a63 --- /dev/null +++ b/Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_camera_move.lua @@ -0,0 +1,182 @@ +-- Basic camera movement that uses the default OpenXR ActionSets asset +-- that ships with the OpenXRVk Gem. +-- This script can be used as an alternative to XrCameraMovementComponent. +local xr_camera_move = { + Properties = { + cameraEntity = {default = EntityId(), + description = "The entity with a camera component"}, + cameraSpeed = { default = 1.0, + suffix= "ms-1", + description="Camera speed."}, + rotationStepSize = { + default = 30.0, + suffix = "deg", + description = "Rotation step size in degrees around Up(Z) Axis. Each time the user moves the right hand thumbstick to the left or to the right the camera will Yaw rotate by this amount of degrees." + } + } +} + +local function DumpActionHandle(name, actionHandle) + if actionHandle:IsValid() then + Debug.Log("Action [" ..name .. "] has index=[" .. actionHandle:GetIndex().. "]") + else + Debug.Log("Action [" ..name .. "] is invalid") + end +end + +local function GetActionHandle(actionSetName, actionName) + local actionHandle = OpenXRActions.GetActionHandle(actionSetName, actionName) + DumpActionHandle(actionName, actionHandle) + assert(actionHandle:IsValid(), "Failed to get action handle [" .. actionName .. "] from action set[" .. actionSetName .. "]") + return actionHandle +end + +local function FilterDeadZone(value, deadzoneMagnitude) + deadzoneMagnitude = deadzoneMagnitude or 0.05 + if math.abs(value) < deadzoneMagnitude then + return 0.0 + end + return value +end + +function xr_camera_move:OnActivate() + assert(EntityId.IsValid(self.Properties.cameraEntity), "xr_camera_move:OnActivate. Invalid camera entity.") + + -- Cache all action handles + local actionSetName = "main_action_set" + self._moveFrontwaysHandle = GetActionHandle(actionSetName, "move_frontways") + self._moveSidewaysHandle = GetActionHandle(actionSetName, "move_sideways") + self._yawRotateHandle = GetActionHandle(actionSetName, "shift_yaw_rotate") + self._moveUpHandle = GetActionHandle(actionSetName, "move_up") + self._moveDownHandle = GetActionHandle(actionSetName, "move_down") + self._aClickHandle = GetActionHandle(actionSetName, "a_button_click") + self._bClickHandle = GetActionHandle(actionSetName, "b_button_click") + + self._cameraMovementStates = Vector3(0.0, 0.0, 0.0) + self._cameraYawRotationState = 0.0 + self._AbsRange = 0.9 + self.tickBusHandler = TickBus.Connect(self); + +end + +function xr_camera_move:OnDeactivate() + if self.tickBusHandler ~= nil then + self.tickBusHandler:Disconnect() + end +end + +function xr_camera_move:_DumpPoses(deltaTime, timePoint) + local outcome = OpenXRReferenceSpaces.GetReferenceSpacePose(self._mySpaceName, "Local") + if outcome:IsSuccess() then + local tm = outcome:GetValue() + Debug.Log("Current transform for <" .. self._mySpaceName .. "> == " .. tostring(tm)) + end + + local headTm = OpenXRReferenceSpaces.GetViewSpacePose() + Debug.Log("View space pose=\n" .. tostring(headTm)) + + local eyeCount = OpenXRReferenceSpaces.GetViewCount() + Debug.Log("Eye count=\n" .. tostring(eyeCount)) + for idx=1, eyeCount do + local eyeTm = OpenXRReferenceSpaces.GetViewPose(idx - 1) + Debug.Log("Eye[" .. tostring(idx) .. "] transform=\n" .. tostring(eyeTm)) + end + + local viewPoses = OpenXRReferenceSpaces.GetViewPoses() + local size = viewPoses:Size() + Debug.Log("Got the following list with " .. tostring(size) .. " eye poses") + for idx=1, size do + local eyeTm = viewPoses:At(idx) + Debug.Log("Eye pose[" .. tostring(idx) .. "] transform=\n" .. tostring(eyeTm)) + end +end + +function xr_camera_move:_ReadActionStates(deltaTime, timePoint) + self._cameraMovementStates = Vector3(0.0, 0.0, 0.0) + + local outcome = OpenXRActions.GetActionStateFloat(self._moveFrontwaysHandle) + if outcome:IsSuccess() then + self._cameraMovementStates.y = FilterDeadZone(outcome:GetValue()) + end + + outcome = OpenXRActions.GetActionStateFloat(self._moveSidewaysHandle) + if outcome:IsSuccess() then + self._cameraMovementStates.x = FilterDeadZone(outcome:GetValue()) + end + + outcome = OpenXRActions.GetActionStateBoolean(self._moveUpHandle) + if outcome:IsSuccess() then + if outcome:GetValue() then + self._cameraMovementStates.z = 1.0 + end + end + + outcome = OpenXRActions.GetActionStateBoolean(self._moveDownHandle) + if outcome:IsSuccess() then + if outcome:GetValue() then + self._cameraMovementStates.z = -1.0 + end + end + + -- Smooth rotation around the Up (Z) axis, also known as Yaw rotation, + -- is prone to motion sickness for most users. + -- So we'll do a rather snappy rotation changes in angular increments. + outcome = OpenXRActions.GetActionStateFloat(self._yawRotateHandle) + if not outcome:IsSuccess() then + return + end + local newYawState = FilterDeadZone(outcome:GetValue()) + if newYawState >= self._AbsRange then + if not self._canRotate then + self._canRotate = true + self._cameraYawRotationState = -1.0 + end + elseif newYawState <= -self._AbsRange then + if not self._canRotate then + self._canRotate = true + self._cameraYawRotationState = 1.0 + end + else + self._canRotate = false + end + +end + +function xr_camera_move:_MoveCamera(deltaTime, timePoint) + local distance = self.Properties.cameraSpeed * deltaTime + local cameraTm = TransformBus.Event.GetWorldTM(self.Properties.cameraEntity) + local upVector = cameraTm:GetBasisZ() + local forwardVector = cameraTm:GetBasisY() + local camVx = cameraTm:GetBasisX() * self._cameraMovementStates.x * distance + local camVy = forwardVector * self._cameraMovementStates.y * distance + local camVz = upVector * self._cameraMovementStates.z * distance + local camDeltaTranslation = camVx + camVy + camVz + + if self._cameraYawRotationState == 0.0 then + TransformBus.Event.SetWorldTranslation(self.Properties.cameraEntity, cameraTm:GetTranslation() + camDeltaTranslation) + return + end + + -- change the camera orientation + local angleRads = math.rad(self.Properties.rotationStepSize) * self._cameraYawRotationState + local rotQuat = Quaternion.CreateFromAxisAngle(upVector, angleRads) + local newForward = rotQuat * forwardVector + newForward:Normalize() + local newBasisX = newForward:Cross(upVector) + newBasisX:Normalize() + + local newLocation = cameraTm:GetTranslation() + camDeltaTranslation + local mat33 = Matrix3x3.CreateFromColumns(newBasisX, newForward, upVector) + local newTm = Transform.CreateFromMatrix3x3AndTranslation(mat33, newLocation) + + TransformBus.Event.SetWorldTM(self.Properties.cameraEntity, newTm) + self._cameraYawRotationState = 0.0 +end + +function xr_camera_move:OnTick(deltaTime, timePoint) + -- self:_DumpPoses(deltaTime, timePoint) + self:_ReadActionStates(deltaTime, timePoint) + self:_MoveCamera(deltaTime, timePoint) +end + +return xr_camera_move \ No newline at end of file diff --git a/Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_spaces_api_test.lua b/Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_spaces_api_test.lua new file mode 100644 index 000000000..9389425f6 --- /dev/null +++ b/Gems/OpenXRVk/Assets/OpenXRVk/Scripts/xr_spaces_api_test.lua @@ -0,0 +1,85 @@ +-- this LUA script shows how to use the OpenXRReferenceSpaces API. +-- It creates a custom reference space called 'MySpace', and prints +-- its pose, along with the pose of the `View` Reference Space pose, +-- and each Eye pose during OnTick(). +local xr_spaces_api_test = { + Properties = { + } +} + +function xr_spaces_api_test:OnActivate() + local spaces = OpenXRReferenceSpaces.GetReferenceSpaceNames() + local size = spaces:Size() + Debug.Log("xr_spaces_api_test:OnActivate Got the following list with " .. tostring(size) .. " spaces") + for idx=1, size do + local name = spaces:At(idx) + Debug.Log("space[" .. tostring(idx) .. "]=" .. name) + end + + local newSpaceName = "MySpace" + local tm = Transform.CreateTranslation(Vector3(0.0, 1.0, 0.0)) + local outcome = OpenXRReferenceSpaces.AddReferenceSpace(OpenXRReferenceSpaces.ReferenceSpaceIdView, newSpaceName, tm) + if outcome:IsSuccess() then + Debug.Log("Sucessfully created space named " .. newSpaceName) + self._mySpaceName = newSpaceName + self.tickBusHandler = TickBus.Connect(self); + else + Debug.Log("Failed to create space named " .. newSpaceName .. ". Reason: " .. outcome:GetError()) + end + + local baseSpaceForViewSpaceName = OpenXRReferenceSpaces.GetBaseSpaceForViewSpacePose() + Debug.Log("FYI: [" .. OpenXRReferenceSpaces.ReferenceSpaceNameView .. "] space will be located with base space [" .. baseSpaceForViewSpaceName .. "]") + + local leftEyeIndex = OpenXRReferenceSpaces.LeftEyeViewId + local rightEyeIndex = OpenXRReferenceSpaces.RightEyeViewId + Debug.Log("FYI: LeftEyeIndex=[" .. tostring(leftEyeIndex) .. "], RightEyeIndex=[" .. tostring(rightEyeIndex) .. "]") + +end + +function xr_spaces_api_test:OnDeactivate() + if self._mySpaceName ~= nil then + local outcome = OpenXRReferenceSpaces.RemoveReferenceSpace(self._mySpaceName) + if outcome:IsSuccess() then + Debug.Log("Sucessfully removed space named " .. self._mySpaceName) + else + Debug.Log("Failed to remove space named " .. self._mySpaceName .. ". Reason: " .. outcome:GetError()) + end + self._mySpaceName = nil + end + + if self.tickBusHandler ~= nil then + self.tickBusHandler:Disconnect() + end +end + +function xr_spaces_api_test:_DumpPoses(deltaTime, timePoint) + local outcome = OpenXRReferenceSpaces.GetReferenceSpacePose(self._mySpaceName, "Local") + if outcome:IsSuccess() then + local tm = outcome:GetValue() + Debug.Log("Current transform for <" .. self._mySpaceName .. "> == " .. tostring(tm)) + end + + local headTm = OpenXRReferenceSpaces.GetViewSpacePose() + Debug.Log("View space pose=\n" .. tostring(headTm)) + + local eyeCount = OpenXRReferenceSpaces.GetViewCount() + Debug.Log("Eye count=\n" .. tostring(eyeCount)) + for idx=1, eyeCount do + local eyeTm = OpenXRReferenceSpaces.GetViewPose(idx - 1) + Debug.Log("Eye[" .. tostring(idx) .. "] transform=\n" .. tostring(eyeTm)) + end + + local viewPoses = OpenXRReferenceSpaces.GetViewPoses() + local size = viewPoses:Size() + Debug.Log("Got the following list with " .. tostring(size) .. " eye poses") + for idx=1, size do + local eyeTm = viewPoses:At(idx) + Debug.Log("Eye pose[" .. tostring(idx) .. "] transform=\n" .. tostring(eyeTm)) + end +end + +function xr_spaces_api_test:OnTick(deltaTime, timePoint) + self:_DumpPoses(deltaTime, timePoint) +end + +return xr_spaces_api_test \ No newline at end of file diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp index 0e448120d..0061c6d4e 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionSetsAsset.cpp @@ -18,8 +18,6 @@ namespace OpenXRVk { - - namespace EditorInternal { // This static asset reference is only relevant when the user is creating an OpenXRActionSetsAsset with the @@ -44,6 +42,7 @@ namespace OpenXRVk } } + static void SetCurrentInteractionProfilesAsset(AZ::Data::Asset& newProfilesAsset, bool loadAsset = true) { @@ -60,6 +59,7 @@ namespace OpenXRVk s_asset = newProfilesAsset; } + static const AZ::Data::Asset& GetCurrentInteractionProfilesAsset(bool loadAsset = true) { if (loadAsset) @@ -105,16 +105,31 @@ namespace OpenXRVk } } + AZStd::string OpenXRActionPathDescriptor::GetEditorText() const { + if (m_interactionProfileName.empty()) + { + return ""; + } + if (m_userPathName.empty()) + { + return ""; + } + if (m_componentPathName.empty()) + { + return ""; + } return AZStd::string::format("%s %s %s", m_interactionProfileName.c_str(), m_userPathName.c_str(), m_componentPathName.c_str()); } + AZ::Crc32 OpenXRActionPathDescriptor::OnInteractionProfileSelected() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } + AZStd::vector OpenXRActionPathDescriptor::GetInteractionProfiles() const { const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset(); @@ -130,11 +145,13 @@ namespace OpenXRVk return profileNames; } + AZ::Crc32 OpenXRActionPathDescriptor::OnUserPathSelected() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } + AZStd::vector OpenXRActionPathDescriptor::GetUserPaths() const { const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset(); @@ -156,11 +173,13 @@ namespace OpenXRVk return retList; } + AZ::Crc32 OpenXRActionPathDescriptor::OnComponentPathSelected() { return AZ::Edit::PropertyRefreshLevels::AttributesAndValues; } + AZStd::vector OpenXRActionPathDescriptor::GetComponentPaths() const { const auto& interactionProfilesAsset = EditorInternal::GetCurrentInteractionProfilesAsset(); @@ -234,6 +253,7 @@ namespace OpenXRVk } } + AZStd::string OpenXRActionDescriptor::GetEditorText() const { // In addition to showing the Action name, we'll append the Action Data Type @@ -294,6 +314,7 @@ namespace OpenXRVk } } + AZStd::string OpenXRActionSetDescriptor::GetEditorText() const { if (!m_name.empty()) @@ -337,6 +358,7 @@ namespace OpenXRVk } } + AZ::Crc32 OpenXRActionSetsAsset::OnInteractionProfilesAssetChanged() { EditorInternal::SetCurrentInteractionProfilesAsset(m_interactionProfilesAsset); @@ -345,6 +367,9 @@ namespace OpenXRVk /// OpenXRActionBindingsAsset /////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////// + /// OpenXRActionBindingsAssetHandler OpenXRActionSetsAssetHandler::OpenXRActionSetsAssetHandler() : AzFramework::GenericAssetHandler( OpenXRActionSetsAsset::s_assetTypeName, @@ -353,6 +378,7 @@ namespace OpenXRVk { } + AZ::Data::AssetHandler::LoadResult OpenXRActionSetsAssetHandler::LoadAssetData( const AZ::Data::Asset& asset, AZStd::shared_ptr stream, @@ -382,6 +408,7 @@ namespace OpenXRVk } + bool OpenXRActionSetsAssetHandler::SaveAssetData(const AZ::Data::Asset& asset, AZ::IO::GenericStream* stream) { OpenXRActionSetsAsset* assetData = asset.GetAs(); @@ -405,5 +432,7 @@ namespace OpenXRVk return AzFramework::GenericAssetHandler::SaveAssetData(asset, stream); } + /// OpenXRActionBindingsAssetHandler + /////////////////////////////////////////////////////////// } // namespace OpenXRVk diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp index 84c49abde..c79b72c3e 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkActionsManager.cpp @@ -61,6 +61,7 @@ namespace OpenXRVk return componentPathDescriptor->GetXrActionType(); } + AZStd::string ActionsManager::GetActionSetsAssetPath() { // Asset Cache relative path @@ -85,6 +86,7 @@ namespace OpenXRVk return AZStd::string(DefaultActionsAssetPath); } + bool ActionsManager::LoadActionSetAssetAsync() { auto productPath = GetActionSetsAssetPath(); @@ -103,6 +105,7 @@ namespace OpenXRVk return true; } + ActionsManager::~ActionsManager() { if (AZ::Data::AssetBus::Handler::BusIsConnected()) @@ -131,6 +134,7 @@ namespace OpenXRVk return LoadActionSetAssetAsync(); } + void ActionsManager::InitInternal() { AZ_Assert(m_actionSetAsset.IsReady(), "Action Sets asset should be ready!"); @@ -203,6 +207,7 @@ namespace OpenXRVk AZ_Printf(LogName, "Successfully configure the Action Sets."); } + bool ActionsManager::SyncActions(XrTime predictedDisplayTime) { if (m_xrActiveActionSets.empty()) @@ -227,7 +232,6 @@ namespace OpenXRVk } - bool ActionsManager::InitActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, const OpenXRActionSetDescriptor& actionSetDescriptor, SuggestedBindingsPerProfile& suggestedBindingsPerProfile) @@ -270,6 +274,7 @@ namespace OpenXRVk return true; } + bool ActionsManager::AddActionToActionSet(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, ActionSetInfo& actionSetInfo, const OpenXRActionDescriptor& actionDescriptor, @@ -359,6 +364,7 @@ namespace OpenXRVk return newXrAction; } + uint32_t ActionsManager::AppendActionBindings(const OpenXRInteractionProfilesAsset& interactionProfilesAsset, const OpenXRActionDescriptor& actionDescriptor, XrAction xrAction, SuggestedBindingsPerProfile& suggestedBindingsPerProfile) const @@ -427,7 +433,6 @@ namespace OpenXRVk } - void ActionsManager::OnInteractionProfileChanged() { if (m_topLevelUserPaths.empty()) @@ -476,6 +481,7 @@ namespace OpenXRVk } } + ///////////////////////////////////////////////// /// OpenXRActionsInterface overrides AZStd::vector ActionsManager::GetAllActionSets() const @@ -489,6 +495,7 @@ namespace OpenXRVk return retList; } + AZ::Outcome ActionsManager::GetActionSetState(const AZStd::string& actionSetName) const { for (size_t i = 0; i < m_actionSets.size(); i++) @@ -503,6 +510,7 @@ namespace OpenXRVk ); } + AZ::Outcome ActionsManager::SetActionSetState(const AZStd::string& actionSetName, bool activate) { for (size_t i = 0; i < m_actionSets.size(); i++) @@ -547,6 +555,7 @@ namespace OpenXRVk ); } + AZ::Outcome ActionsManager::GetActionStateBoolean(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) @@ -573,6 +582,7 @@ namespace OpenXRVk return AZ::Success(state.currentState); } + AZ::Outcome ActionsManager::GetActionStateFloat(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) @@ -599,6 +609,7 @@ namespace OpenXRVk return AZ::Success(state.currentState); } + AZ::Outcome ActionsManager::GetActionStateVector2(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) @@ -625,6 +636,7 @@ namespace OpenXRVk return AZ::Success(AZ::Vector2(state.currentState.x, state.currentState.y)); } + AZ::Outcome ActionsManager::SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName) { if (visualizedSpaceName == m_baseReferenceSpaceName) @@ -652,11 +664,13 @@ namespace OpenXRVk return AZ::Success(true); } + const AZStd::string& ActionsManager::GetBaseReferenceSpaceForPoseActions() const { return m_baseReferenceSpaceName; } + AZ::Outcome ActionsManager::GetActionStatePose(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) @@ -704,6 +718,7 @@ namespace OpenXRVk return AZ::Success(retPoseTransform); } + AZ::Outcome ActionsManager::GetActionStatePoseWithVelocities(ActionHandle actionHandle) const { if (!actionHandle.IsValid()) @@ -775,6 +790,7 @@ namespace OpenXRVk return AZ::Success(true); } + AZ::Outcome ActionsManager::StopHapticVibrationAction(ActionHandle actionHandle) { if (!actionHandle.IsValid()) @@ -828,6 +844,7 @@ namespace OpenXRVk }); } + void ActionsManager::OnAssetError(AZ::Data::Asset asset) { AZ::Data::AssetBus::Handler::BusDisconnect(); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp index 717723abf..b8113834a 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkAssetsValidator.cpp @@ -96,6 +96,7 @@ namespace OpenXRVkAssetsValidator return AZ::Success(); } + // An OpenXR path string only contain characters as described here // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings static AZ::Outcome ValidateOpenXRPath(const AZStd::string& path) @@ -110,6 +111,7 @@ namespace OpenXRVkAssetsValidator return AZ::Success(); } + static AZ::Outcome ValidateComponentPathDescriptor(const OpenXRVk::OpenXRInteractionComponentPathDescriptor& componentPathDescriptor, AZStd::unordered_set& uniqueComponentPathNames, AZStd::unordered_set& uniqueComponentPathPaths) { @@ -384,6 +386,7 @@ namespace OpenXRVkAssetsValidator return AZ::Success(); } + static const AZStd::string& GetActionTypeStringFromActionPathDescriptor( const OpenXRVk::OpenXRInteractionProfilesAsset& interactionProfilesAsset, const OpenXRVk::OpenXRActionPathDescriptor& actionPathDescriptor @@ -396,6 +399,7 @@ namespace OpenXRVkAssetsValidator ); } + static bool IsActionTypeBoolOrFloat(const AZStd::string& actionTypeStr) { return ( @@ -404,6 +408,7 @@ namespace OpenXRVkAssetsValidator ); } + static bool AreCompatibleActionTypeStrings(const AZStd::string& lhs, const AZStd::string& rhs) { if (IsActionTypeBoolOrFloat(lhs) && IsActionTypeBoolOrFloat(rhs)) @@ -413,6 +418,7 @@ namespace OpenXRVkAssetsValidator return (lhs == rhs); } + // An OpenXR name string only contain characters which are allowed in a SINGLE LEVEL of a well-formed path string // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#well-formed-path-strings static AZ::Outcome ValidateOpenXRName(const AZStd::string& name) diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp index dd8bf4411..9c953c73b 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkBehaviorReflection.cpp @@ -37,6 +37,7 @@ namespace OpenXRVk } } + class OpenXRReferenceSpaces { public: @@ -59,6 +60,7 @@ namespace OpenXRVk return iface->GetReferenceSpaceNames(); } + static AZ::Outcome AddReferenceSpace(IOpenXRReferenceSpaces::ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) { @@ -72,6 +74,7 @@ namespace OpenXRVk return iface->AddReferenceSpace(referenceSpaceType, spaceName, poseInReferenceSpace); } + static AZ::Outcome RemoveReferenceSpace(const AZStd::string& spaceName) { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -84,6 +87,7 @@ namespace OpenXRVk return iface->RemoveReferenceSpace(spaceName); } + static AZ::Outcome GetReferenceSpacePose(const AZStd::string& spaceName, const AZStd::string& baseSpaceName) { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -96,6 +100,7 @@ namespace OpenXRVk return iface->GetReferenceSpacePose(spaceName, baseSpaceName); } + static AZ::Outcome SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -108,6 +113,7 @@ namespace OpenXRVk return iface->SetBaseSpaceForViewSpacePose(spaceName); } + static const AZStd::string& GetBaseSpaceForViewSpacePose() { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -120,6 +126,7 @@ namespace OpenXRVk return iface->GetBaseSpaceForViewSpacePose(); } + static const AZ::Transform& GetViewSpacePose() { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -131,6 +138,7 @@ namespace OpenXRVk return iface->GetViewSpacePose(); } + static uint32_t GetViewCount() { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -142,6 +150,7 @@ namespace OpenXRVk return iface->GetViewCount(); } + static const AZ::Transform& GetViewPose(uint32_t eyeIndex) { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -166,6 +175,7 @@ namespace OpenXRVk return iface->GetViewFovData(eyeIndex); } + static const AZStd::vector& GetViewPoses() { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -178,6 +188,7 @@ namespace OpenXRVk return iface->GetViewPoses(); } + static void ForceViewPosesCacheUpdate() { const auto iface = OpenXRReferenceSpacesInterface::Get(); @@ -202,6 +213,7 @@ namespace OpenXRVk OpenXRActions() = default; ~OpenXRActions() = default; + static AZStd::vector GetAllActionSets() { const auto iface = OpenXRActionsInterface::Get(); @@ -213,6 +225,7 @@ namespace OpenXRVk return iface->GetAllActionSets(); } + static AZ::Outcome GetActionSetState(const AZStd::string& actionSetName) { const auto iface = OpenXRActionsInterface::Get(); @@ -224,6 +237,7 @@ namespace OpenXRVk return iface->GetActionSetState(actionSetName); } + static AZ::Outcome SetActionSetState(const AZStd::string& actionSetName, bool activate) { const auto iface = OpenXRActionsInterface::Get(); @@ -235,6 +249,7 @@ namespace OpenXRVk return iface->SetActionSetState(actionSetName, activate); } + //! REMARK: The original idea was to return an: //! AZ::Outcome //! But when compiling for Android the Behavior Context ->Method() would statically @@ -257,6 +272,7 @@ namespace OpenXRVk return {}; } + static AZ::Outcome GetActionStateBoolean(IOpenXRActions::ActionHandle actionHandle) { const auto iface = OpenXRActionsInterface::Get(); @@ -269,6 +285,7 @@ namespace OpenXRVk return iface->GetActionStateBoolean(actionHandle); } + static AZ::Outcome GetActionStateFloat(IOpenXRActions::ActionHandle actionHandle) { const auto iface = OpenXRActionsInterface::Get(); @@ -281,6 +298,7 @@ namespace OpenXRVk return iface->GetActionStateFloat(actionHandle); } + static AZ::Outcome GetActionStateVector2(IOpenXRActions::ActionHandle actionHandle) { const auto iface = OpenXRActionsInterface::Get(); @@ -293,6 +311,7 @@ namespace OpenXRVk return iface->GetActionStateVector2(actionHandle); } + static AZ::Outcome SetBaseReferenceSpaceForPoseActions(const AZStd::string& visualizedSpaceName) { const auto iface = OpenXRActionsInterface::Get(); @@ -305,6 +324,7 @@ namespace OpenXRVk return iface->SetBaseReferenceSpaceForPoseActions(visualizedSpaceName); } + static const AZStd::string& GetBaseReferenceSpaceForPoseActions() { const auto iface = OpenXRActionsInterface::Get(); @@ -317,6 +337,7 @@ namespace OpenXRVk return iface->GetBaseReferenceSpaceForPoseActions(); } + static AZ::Outcome GetActionStatePose(IOpenXRActions::ActionHandle actionHandle) { const auto iface = OpenXRActionsInterface::Get(); @@ -329,6 +350,7 @@ namespace OpenXRVk return iface->GetActionStatePose(actionHandle); } + static AZ::Outcome GetActionStatePoseWithVelocities(IOpenXRActions::ActionHandle actionHandle) { const auto iface = OpenXRActionsInterface::Get(); @@ -341,6 +363,7 @@ namespace OpenXRVk return iface->GetActionStatePoseWithVelocities(actionHandle); } + static AZ::Outcome ApplyHapticVibrationAction(IOpenXRActions::ActionHandle actionHandle, uint64_t durationNanos, float frequencyHz, float amplitude) { const auto iface = OpenXRActionsInterface::Get(); @@ -353,6 +376,7 @@ namespace OpenXRVk return iface->ApplyHapticVibrationAction(actionHandle, durationNanos, frequencyHz, amplitude); } + static AZ::Outcome StopHapticVibrationAction(IOpenXRActions::ActionHandle actionHandle) { const auto iface = OpenXRActionsInterface::Get(); @@ -367,6 +391,7 @@ namespace OpenXRVk }; // class OpenXRActions + void OpenXRBehaviorReflect(AZ::BehaviorContext& context) { IOpenXRActions::ActionHandle::Reflect(&context); diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp index 9df1ed1f1..04105a192 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInteractionProfilesAsset.cpp @@ -37,11 +37,13 @@ namespace OpenXRVk return XR_ACTION_TYPE_MAX_ENUM; } + XrActionType OpenXRInteractionComponentPathDescriptor::GetXrActionType() const { return GetXrActionType(m_actionTypeStr); } + static AZStd::vector GetEditorXrActionTypeNames() { static AZStd::vector s_actionTypeNames = { @@ -54,6 +56,7 @@ namespace OpenXRVk return s_actionTypeNames; } + void OpenXRInteractionComponentPathDescriptor::Reflect(AZ::ReflectContext* context) { AZ::SerializeContext* serialize = azrtti_cast(context); @@ -83,6 +86,7 @@ namespace OpenXRVk } } + AZStd::string OpenXRInteractionComponentPathDescriptor::GetEditorText() { return m_name.empty() ? "" : m_name; @@ -122,11 +126,13 @@ namespace OpenXRVk } } + AZStd::string OpenXRInteractionUserPathDescriptor::GetEditorText() { return m_name.empty() ? "" : m_name; } + const OpenXRInteractionComponentPathDescriptor* OpenXRInteractionUserPathDescriptor::GetComponentPathDescriptor(const AZStd::string& componentPathName) const { for (const auto& componentPathDescriptor : m_componentPathDescriptors) @@ -179,6 +185,7 @@ namespace OpenXRVk } } + AZStd::string OpenXRInteractionProfileDescriptor::GetEditorText() { return m_name.empty() ? "" : m_name; @@ -197,6 +204,7 @@ namespace OpenXRVk return nullptr; } + const OpenXRInteractionComponentPathDescriptor* OpenXRInteractionProfileDescriptor::GetCommonComponentPathDescriptor(const AZStd::string& componentPathName) const { for (const auto& componentPathDescriptor : m_commonComponentPathDescriptors) @@ -209,6 +217,7 @@ namespace OpenXRVk return nullptr; } + const OpenXRInteractionComponentPathDescriptor* OpenXRInteractionProfileDescriptor::GetComponentPathDescriptor(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, const AZStd::string& componentPathName) const { @@ -221,6 +230,7 @@ namespace OpenXRVk return componentPathDescriptor; } + AZStd::string OpenXRInteractionProfileDescriptor::GetComponentAbsolutePath(const OpenXRInteractionUserPathDescriptor& userPathDescriptor, const AZStd::string& componentPathName) const { @@ -264,6 +274,7 @@ namespace OpenXRVk } } + const OpenXRInteractionProfileDescriptor* OpenXRInteractionProfilesAsset::GetInteractionProfileDescriptor(const AZStd::string& profileName) const { for (const auto& profileDescriptor : m_interactionProfileDescriptors) @@ -276,6 +287,7 @@ namespace OpenXRVk return nullptr; } + const AZStd::string& OpenXRInteractionProfilesAsset::GetActionPathTypeStr(const AZStd::string& profileName, const AZStd::string& userPathName, const AZStd::string& componentPathName) const { static const AZStd::string emptyStr; diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp index ebc75efe6..476b930a1 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkReferenceSpacesManager.cpp @@ -58,6 +58,7 @@ namespace OpenXRVk return true; } + bool ReferenceSpacesManager::SyncViews(XrTime predictedDisplayTime) { m_predictedDisplaytime = predictedDisplayTime; @@ -75,6 +76,7 @@ namespace OpenXRVk return true; } + void ReferenceSpacesManager::OnSessionReady() { // REMARK: On previous versions of the OpenXRVk::Spaces API it was necessary @@ -86,11 +88,13 @@ namespace OpenXRVk AZ_Printf(LogName, "%s. For now, this function does nothing.\n", __FUNCTION__); } + const AZStd::vector& ReferenceSpacesManager::GetXrViews() const { return m_xrViews; } + XrSpace ReferenceSpacesManager::GetViewSpaceXrSpace() const { return m_viewSpace->m_xrSpace; @@ -107,6 +111,7 @@ namespace OpenXRVk return retList; } + AZ::Outcome ReferenceSpacesManager::AddReferenceSpace(ReferenceSpaceId referenceSpaceType, const AZStd::string& spaceName, const AZ::Transform& poseInReferenceSpace) { @@ -131,6 +136,7 @@ namespace OpenXRVk return AZ::Success(true); } + AZ::Outcome ReferenceSpacesManager::RemoveReferenceSpace(const AZStd::string& spaceName) { static const AZStd::unordered_set defaultSystemSpaces { @@ -165,6 +171,7 @@ namespace OpenXRVk return AZ::Success(true); } + const void * ReferenceSpacesManager::GetReferenceSpaceNativeHandle(const AZStd::string& spaceName) const { const auto spaceItor = m_spaces.find(spaceName); @@ -206,6 +213,7 @@ namespace OpenXRVk return AZ::Success(AzTransformFromXrPose(xrSpaceLocation.pose)); } + AZ::Outcome ReferenceSpacesManager::SetBaseSpaceForViewSpacePose(const AZStd::string& spaceName) { const auto baseSpaceItor = m_spaces.find(spaceName); @@ -221,22 +229,26 @@ namespace OpenXRVk return AZ::Success(true); } + const AZStd::string& ReferenceSpacesManager::GetBaseSpaceForViewSpacePose() const { AZ_Assert(m_baseSpaceForViewSpace != nullptr, "A base space is always expected to exist!"); return m_baseSpaceForViewSpace->m_name; } + const AZ::Transform& ReferenceSpacesManager::GetViewSpacePose() const { return m_viewSpacePose; } + uint32_t ReferenceSpacesManager::GetViewCount() const { return aznumeric_cast(m_eyeViewPoses.size()); } + const AZ::Transform& ReferenceSpacesManager::GetViewPose(uint32_t eyeIndex) const { if (eyeIndex >= m_eyeViewPoses.size()) @@ -251,6 +263,7 @@ namespace OpenXRVk return m_eyeViewPoses[eyeIndex]; } + const AZ::RPI::FovData& ReferenceSpacesManager::GetViewFovData(uint32_t eyeIndex) const { if (eyeIndex >= m_eyeViewPoses.size()) @@ -266,11 +279,13 @@ namespace OpenXRVk return m_eyeViewFovDatas[eyeIndex]; } + const AZStd::vector& ReferenceSpacesManager::GetViewPoses() const { return m_eyeViewPoses; } + void ReferenceSpacesManager::ForceViewPosesCacheUpdate() { XrViewState viewState{ XR_TYPE_VIEW_STATE }; From 852b9d018e4081a63001b7b19a9ea3bc5713bb08 Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:34:06 -0600 Subject: [PATCH 32/33] Saving the work. Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp | 5 ++++- Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp index f97fe6fba..dee4a9364 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkAssetsBuilder.cpp @@ -29,6 +29,7 @@ namespace OpenXRVkBuilders return AZStd::unique_ptr(actionSetsAssetPtr); } + void OpenXRAssetsBuilder::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { //! First get the extension @@ -91,7 +92,6 @@ namespace OpenXRVkBuilders } - void OpenXRAssetsBuilder::ProcessInteractionProfilesAssetJob([[maybe_unused]] const AssetBuilderSDK::ProcessJobRequest& request, [[maybe_unused]] AssetBuilderSDK::ProcessJobResponse& response) const { // Open the file, and make sure there's no redundant data, the OpenXR Paths are well formatted, etc. @@ -162,6 +162,7 @@ namespace OpenXRVkBuilders return sourcePath; } + void OpenXRAssetsBuilder::CreateActionSetsAssetJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response) const { // Make sure the InteractionProfiles asset referenced in this ActionSets asset exists. and if so, @@ -208,6 +209,7 @@ namespace OpenXRVkBuilders response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; } + //! Each action in an actionSet has a "name" and a "localizedName". The "name" can never be empty, but //! if "localizedName" is empty we automatically patch it as an identical copy of "name". static void FixEmptyLocalizedNames(OpenXRVk::OpenXRActionSetsAsset& actionSetAsset) @@ -231,6 +233,7 @@ namespace OpenXRVkBuilders } } + void OpenXRAssetsBuilder::ProcessActionSetsAssetJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response) const { auto actionSetsAssetPtr = LoadAssetAsUniquePtr(request.m_fullPath); diff --git a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp index 8e08faadc..2c17d86dd 100644 --- a/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp +++ b/Gems/OpenXRVk/Code/Source/Builders/OpenXRVkBuilderModule.cpp @@ -9,7 +9,7 @@ #include #include -#include "OpenXRVkAssetBuildersSystemComponent.h" +#include "OpenXRVkAssetsBuilderSystemComponent.h" namespace OpenXRVkBuilders { From d63739213f50b47b12afaa0d84b7e7dcaf14fc5f Mon Sep 17 00:00:00 2001 From: galibzon <66021303+galibzon@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:09:22 -0600 Subject: [PATCH 33/33] Saving the work Signed-off-by: galibzon <66021303+galibzon@users.noreply.github.com> --- Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp b/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp index 4433da572..4e3e79b35 100644 --- a/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp +++ b/Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp @@ -163,6 +163,15 @@ namespace OpenXRVk //TODO::Add support for handheld display m_formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO }; + systemInfo.formFactor = m_formFactor; + result = xrGetSystem(m_xrInstance, &systemInfo, &m_xrSystemId); + if (IsError(result)) + { + AZ_Warning("OpenXRVk", false, "Failed to get XR System id"); + return AZ::RHI::ResultCode::Fail; + } + //TODO::Add support for other view configuration types m_viewConfigType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; m_viewCount = 0; @@ -177,15 +186,6 @@ namespace OpenXRVk //TODO::Add support for other environment blend types m_environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; - XrSystemGetInfo systemInfo{ XR_TYPE_SYSTEM_GET_INFO }; - systemInfo.formFactor = m_formFactor; - result = xrGetSystem(m_xrInstance, &systemInfo, &m_xrSystemId); - if (IsError(result)) - { - AZ_Warning("OpenXRVk", false, "Failed to get XR System id"); - return AZ::RHI::ResultCode::Fail; - } - // Query the runtime Vulkan API version requirements XrGraphicsRequirementsVulkan2KHR graphicsRequirements{ XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR }; PFN_xrGetVulkanGraphicsRequirementsKHR pfnGetVulkanGraphicsRequirementsKHR = nullptr;