Przeglądaj źródła

Merge remote-tracking branch 'upstream/development' into merge_ros2_gem

Signed-off-by: Steve Pham <[email protected]>
Steve Pham 2 lat temu
rodzic
commit
ed3ab137da
100 zmienionych plików z 3948 dodań i 380 usunięć
  1. 20 0
      .automatedtesting.json
  2. 35 0
      .github/CODEOWNERS
  3. 3 0
      .gitignore
  4. 20 0
      Gems/AzQtComponentsForPython/CMakeLists.txt
  5. 48 0
      Gems/AzQtComponentsForPython/Code/CMakeLists.txt
  6. 9 0
      Gems/AzQtComponentsForPython/Code/Platform/Android/PAL_android.cmake
  7. 10 0
      Gems/AzQtComponentsForPython/Code/Platform/Linux/PAL_linux.cmake
  8. 9 0
      Gems/AzQtComponentsForPython/Code/Platform/Mac/PAL_mac.cmake
  9. 9 0
      Gems/AzQtComponentsForPython/Code/Platform/Windows/PAL_windows.cmake
  10. 9 0
      Gems/AzQtComponentsForPython/Code/Platform/iOS/PAL_ios.cmake
  11. 80 0
      Gems/AzQtComponentsForPython/Code/bindings_azqtcomponents.h
  12. 295 0
      Gems/AzQtComponentsForPython/Code/bindings_azqtcomponents.xml
  13. 177 0
      Gems/AzQtComponentsForPython/Code/generated_files.cmake
  14. 8 0
      Gems/AzQtComponentsForPython/Code/license_header.txt
  15. 23 0
      Gems/AzQtComponentsForPython/gem.json
  16. 3 0
      Gems/AzQtComponentsForPython/preview.png
  17. 9 1
      Gems/OpenXRVk/3rdParty/Platform/Android/BuiltInPackages_android.cmake
  18. 42 0
      Gems/OpenXRVk/3rdParty/Platform/Android/FindOpenXROculus.cmake
  19. 2 0
      Gems/OpenXRVk/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake
  20. 2 0
      Gems/OpenXRVk/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake
  21. 37 3
      Gems/OpenXRVk/Code/CMakeLists.txt
  22. 468 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/InputDeviceXRController.h
  23. 5 3
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h
  24. 66 26
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInput.h
  25. 8 8
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h
  26. 1 1
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkPhysicalDevice.h
  27. 18 8
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h
  28. 3 4
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSpace.h
  29. 4 7
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSwapChain.h
  30. 12 9
      Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h
  31. 9 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/OpenXRVk_Platform.h
  32. 1 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/platform_private_mac_files.cmake
  33. 9 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/OpenXRVk_Platform.h
  34. 1 0
      Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/platform_private_ios_files.cmake
  35. 759 0
      Gems/OpenXRVk/Code/Source/InputDeviceXRController.cpp
  36. 28 0
      Gems/OpenXRVk/Code/Source/OpenXRVkCommon.h
  37. 53 33
      Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp
  38. 382 139
      Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp
  39. 32 7
      Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp
  40. 3 1
      Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp
  41. 7 13
      Gems/OpenXRVk/Code/Source/OpenXRVkPhysicalDevice.cpp
  42. 70 13
      Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp
  43. 5 5
      Gems/OpenXRVk/Code/Source/OpenXRVkSpace.cpp
  44. 38 17
      Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp
  45. 5 1
      Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp
  46. 10 0
      Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp
  47. 74 0
      Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVkCommon_Android.cpp
  48. 2 0
      Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVk_Traits_Android.h
  49. 4 0
      Gems/OpenXRVk/Code/Source/Platform/Android/platform_private_android_files.cmake
  50. 11 0
      Gems/OpenXRVk/Code/Source/Platform/Android/platform_test_android_files.cmake
  51. 23 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Default/InputDeviceXRController_Default.cpp
  52. 161 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.cpp
  53. 62 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.h
  54. 20 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/InputDeviceXRController_Unimplemented.cpp
  55. 29 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/OpenXRVkCommon_Unimplemented.cpp
  56. 24 0
      Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/OpenXRVkTest_Unimplemented.cpp
  57. 2 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/OpenXRVk_Traits_Linux.h
  58. 1 1
      Gems/OpenXRVk/Code/Source/Platform/Linux/PAL_linux.cmake
  59. 4 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/platform_private_linux_files.cmake
  60. 11 0
      Gems/OpenXRVk/Code/Source/Platform/Linux/platform_test_linux_files.cmake
  61. 10 0
      Gems/OpenXRVk/Code/Source/Platform/Mac/OpenXRVk_Traits_Mac.h
  62. 10 0
      Gems/OpenXRVk/Code/Source/Platform/Mac/OpenXRVk_Traits_Platform.h
  63. 14 0
      Gems/OpenXRVk/Code/Source/Platform/Mac/platform_private_mac_files.cmake
  64. 8 0
      Gems/OpenXRVk/Code/Source/Platform/Mac/platform_private_static_mac.cmake
  65. 11 0
      Gems/OpenXRVk/Code/Source/Platform/Mac/platform_test_mac_files.cmake
  66. 2 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/OpenXRVk_Traits_Windows.h
  67. 4 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/platform_private_windows_files.cmake
  68. 11 0
      Gems/OpenXRVk/Code/Source/Platform/Windows/platform_test_windows_files.cmake
  69. 10 0
      Gems/OpenXRVk/Code/Source/Platform/iOS/OpenXRVk_Traits_Platform.h
  70. 10 0
      Gems/OpenXRVk/Code/Source/Platform/iOS/OpenXRVk_Traits_iOS.h
  71. 14 0
      Gems/OpenXRVk/Code/Source/Platform/iOS/platform_private_ios_files.cmake
  72. 8 0
      Gems/OpenXRVk/Code/Source/Platform/iOS/platform_private_static_ios.cmake
  73. 11 0
      Gems/OpenXRVk/Code/Source/Platform/iOS/platform_test_ios_files.cmake
  74. 166 0
      Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp
  75. 59 0
      Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.h
  76. 40 0
      Gems/OpenXRVk/Code/Tests/OpenXRVkTests.cpp
  77. 24 0
      Gems/OpenXRVk/Code/Tests/OpenXRVkTests.h
  78. 5 0
      Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake
  79. 12 0
      Gems/OpenXRVk/Code/openxrvk_tests_files.cmake
  80. 7 0
      Gems/OpenXRVk/External/OculusOpenXRMobileSDK/README.md
  81. 31 1
      Gems/XR/Code/CMakeLists.txt
  82. 5 4
      Gems/XR/Code/Include/XR/XRBase.h
  83. 23 12
      Gems/XR/Code/Include/XR/XRDevice.h
  84. 4 7
      Gems/XR/Code/Include/XR/XRInput.h
  85. 12 12
      Gems/XR/Code/Include/XR/XRInstance.h
  86. 1 1
      Gems/XR/Code/Include/XR/XRObject.h
  87. 36 7
      Gems/XR/Code/Include/XR/XRSession.h
  88. 2 3
      Gems/XR/Code/Include/XR/XRSpace.h
  89. 16 14
      Gems/XR/Code/Include/XR/XRSwapChain.h
  90. 28 14
      Gems/XR/Code/Include/XR/XRSystem.h
  91. 2 2
      Gems/XR/Code/Include/XR/XRSystemComponent.h
  92. 8 2
      Gems/XR/Code/Include/XR/XRUtils.h
  93. 1 0
      Gems/XR/Code/Source/Platform/Android/XR_Traits_Android.h
  94. 11 0
      Gems/XR/Code/Source/Platform/Android/platform_test_android_files.cmake
  95. 24 0
      Gems/XR/Code/Source/Platform/Common/Unimplemented/XRTest_Unimplemented.cpp
  96. 1 1
      Gems/XR/Code/Source/Platform/Linux/PAL_linux.cmake
  97. 1 0
      Gems/XR/Code/Source/Platform/Linux/XR_Traits_Linux.h
  98. 11 0
      Gems/XR/Code/Source/Platform/Linux/platform_test_linux_files.cmake
  99. 10 0
      Gems/XR/Code/Source/Platform/Mac/XR_Traits_Mac.h
  100. 10 0
      Gems/XR/Code/Source/Platform/Mac/XR_Traits_Platform.h

+ 20 - 0
.automatedtesting.json

@@ -0,0 +1,20 @@
+{
+    "REGISTER_ENGINES": [
+    ],
+    "REGISTER_PROJECTS": [
+        "Projects/OpenXRTest"
+    ],
+    "REGISTER_GEMS": [
+        "Gems/XR",
+        "Gems/OpenXRVk"
+    ],
+    "REGISTER_TEMPLATES": [
+        "Templates/Multiplayer"
+    ],
+    "REGISTER_RESTRICTED": [
+    ],
+    "ENABLE_GEMS": [
+        "Gems/XR",
+        "Gems/OpenXRVk"
+    ]
+}

+ 35 - 0
.github/CODEOWNERS

@@ -0,0 +1,35 @@
+# SIG-Build (@o3de/sig-build-maintainers @o3de/sig-build-reviewers)
+
+# SIG-Content (@o3de/sig-content-maintainers @o3de/sig-content-reviewers)
+
+# SIG-Core (@o3de/sig-core-reviewers @o3de/sig-core-maintainers)
+
+# SIG-Docs-Community
+
+# SIG-Graphics-Audio (@o3de/sig-graphics-audio-maintainers @o3de/sig-graphics-audio-reviewers)
+
+# SIG-Network (@o3de/sig-network-maintainers)
+Templates/Multiplayer/ @o3de/sig-network-maintainers
+
+# SIG-Operations
+
+# SIG-Platform (@o3de/sig-platform-reviewers @o3de/sig-platform-maintainers)
+Gems/OpenXRVk/ @o3de/sig-platform-reviewers @o3de/sig-platform-maintainers
+Gems/XR/ @o3de/sig-platform-reviewers @o3de/sig-platform-maintainers
+Projects/OpenXRTest/ @o3de/sig-platform-reviewers @o3de/sig-platform-maintainers
+
+# SIG-Release
+
+# SIG-Security
+
+# SIG-Simulation (@o3de/sig-simulation-reviewers @o3de/sig-simulation-maintainers)
+
+# SIG-Testing (@o3de/sig-testing-reviewers)
+
+# SIG-UI-UX
+
+
+
+
+
+

+ 3 - 0
.gitignore

@@ -21,3 +21,6 @@ _savebackup/
 *.swatches
 /imgui.ini
 
+# Ignore Oculus OpenXR Mobile SDK files except the README.md file
+Gems/OpenXRVk/External/OculusOpenXRMobileSDK/**
+!Gems/OpenXRVk/External/OculusOpenXRMobileSDK/README.md

+ 20 - 0
Gems/AzQtComponentsForPython/CMakeLists.txt

@@ -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
+#
+#
+
+# Query the gem name from the gem.json file if possible
+# otherwise fallback to using ${Name}
+o3de_find_ancestor_gem_root(gempath gem_name "${CMAKE_CURRENT_SOURCE_DIR}")
+if (NOT gem_name)
+    set(gem_name "AzQtComponentsForPython")
+endif()
+
+set(gem_path ${CMAKE_CURRENT_LIST_DIR})
+set(gem_json ${gem_path}/gem.json)
+o3de_restricted_path(${gem_json} gem_restricted_path gem_parent_relative_path)
+
+add_subdirectory(Code)

+ 48 - 0
Gems/AzQtComponentsForPython/Code/CMakeLists.txt

@@ -0,0 +1,48 @@
+#
+# 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
+#
+#
+
+o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} "${gem_restricted_path}" "${gem_path}" "${gem_parent_relative_path}")
+
+include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
+
+if (PAL_TRAIT_AZQTCOMPONENTSFORPYTHON_SUPPORTED)   
+    find_package(Qt5 5.12 REQUIRED COMPONENTS Core Gui Widgets)
+    
+    # Find the qt package location.
+    ly_get_package_association("Qt" QT_PACKAGE_NAME)
+    ly_package_get_target_folder(${QT_PACKAGE_NAME} QT_DOWNLOAD_LOCATION)
+    set(QT_MODULE_DIR ${QT_DOWNLOAD_LOCATION}/${QT_PACKAGE_NAME}/qt)
+                     
+    # The library we're creating bindings for.
+    set(AZQTCOMPONENTS_LIBRARY "AzQtComponents")
+        
+    #Define the shared library for which we will create bindings.
+    set_property(TARGET ${AZQTCOMPONENTS_LIBRARY} PROPERTY PREFIX "")
+    
+    add_shiboken_project(
+        NAME AzQtComponentsForPython
+        NAMESPACE Gem
+        MODULE_NAME azqtpyside
+        WRAPPED_HEADER ${CMAKE_CURRENT_LIST_DIR}/bindings_azqtcomponents.h
+        TYPESYSTEM_FILE ${CMAKE_CURRENT_LIST_DIR}/bindings_azqtcomponents.xml
+        GENERATED_FILES ${CMAKE_CURRENT_LIST_DIR}/generated_files.cmake
+        LICENSE_HEADER ${CMAKE_CURRENT_LIST_DIR}/license_header.txt
+        DEPENDENCIES 
+            ${AZQTCOMPONENTS_LIBRARY}
+            Gem::EditorPythonBindings.Static
+        INCLUDE_DIRS 
+            ${LY_ROOT_FOLDER}/Code/Framework/AzCore 
+            ${LY_ROOT_FOLDER}/Code/Framework/AzQtComponents
+            ${LY_ROOT_FOLDER}/Code/Framework/AzCore/Platform/${PAL_PLATFORM_NAME} 
+            ${QT_MODULE_DIR}/include
+            ${QT_MODULE_DIR}/include/QtWidgets
+            ${QT_MODULE_DIR}/include/QtCore
+            ${QT_MODULE_DIR}/include/QtGui
+    )
+    
+endif()

+ 9 - 0
Gems/AzQtComponentsForPython/Code/Platform/Android/PAL_android.cmake

@@ -0,0 +1,9 @@
+#
+# 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(PAL_TRAIT_AZQTCOMPONENTSFORPYTHON_SUPPORTED FALSE)

+ 10 - 0
Gems/AzQtComponentsForPython/Code/Platform/Linux/PAL_linux.cmake

@@ -0,0 +1,10 @@
+#
+# 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(PAL_TRAIT_AZQTCOMPONENTSFORPYTHON_SUPPORTED TRUE)
+

+ 9 - 0
Gems/AzQtComponentsForPython/Code/Platform/Mac/PAL_mac.cmake

@@ -0,0 +1,9 @@
+#
+# 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(PAL_TRAIT_AZQTCOMPONENTSFORPYTHON_SUPPORTED FALSE)

+ 9 - 0
Gems/AzQtComponentsForPython/Code/Platform/Windows/PAL_windows.cmake

@@ -0,0 +1,9 @@
+#
+# 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(PAL_TRAIT_AZQTCOMPONENTSFORPYTHON_SUPPORTED TRUE)

+ 9 - 0
Gems/AzQtComponentsForPython/Code/Platform/iOS/PAL_ios.cmake

@@ -0,0 +1,9 @@
+#
+# 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(PAL_TRAIT_AZQTCOMPONENTSFORPYTHON_SUPPORTED FALSE)

+ 80 - 0
Gems/AzQtComponentsForPython/Code/bindings_azqtcomponents.h

@@ -0,0 +1,80 @@
+/*
+ * 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 <AzQtComponents/Components/ButtonDivider.h>
+#include <AzQtComponents/Components/ButtonStripe.h>
+#include <AzQtComponents/Components/ConfigHelpers.h>
+#include <AzQtComponents/Components/DockBar.h>
+#include <AzQtComponents/Components/DockBarButton.h>
+#include <AzQtComponents/Components/DockMainWindow.h>
+#include <AzQtComponents/Components/DockTabBar.h>
+#include <AzQtComponents/Components/DockTabWidget.h>
+#include <AzQtComponents/Components/ExtendedLabel.h>    
+#include <AzQtComponents/Components/FancyDocking.h>
+#include <AzQtComponents/Components/FancyDockingDropZoneWidget.h>
+#include <AzQtComponents/Components/FancyDockingGhostWidget.h>
+#include <AzQtComponents/Components/FlowLayout.h>
+#include <AzQtComponents/Components/FilteredSearchWidget.h>
+#include <AzQtComponents/Components/GlobalEventFilter.h>
+#include <AzQtComponents/Components/HelpButton.h>
+#include <AzQtComponents/Components/InteractiveWindowGeometryChanger.h>
+#include <AzQtComponents/Components/SearchLineEdit.h>
+#include <AzQtComponents/Components/Style.h>
+#include <AzQtComponents/Components/StyledBusyLabel.h>
+#include <AzQtComponents/Components/StyledDetailsTableModel.h>
+#include <AzQtComponents/Components/StyledDetailsTableView.h>
+#include <AzQtComponents/Components/StyledDialog.h>
+#include <AzQtComponents/Components/StyledDockWidget.h>
+#include <AzQtComponents/Components/StyledLineEdit.h>
+#include <AzQtComponents/Components/StyledSpinBox.h>
+#include <AzQtComponents/Components/StyleManager.h>
+#include <AzQtComponents/Components/StyleSheetCache.h>
+#include <AzQtComponents/Components/StylesheetPreprocessor.h>
+#include <AzQtComponents/Components/TagSelector.h>
+#include <AzQtComponents/Components/Titlebar.h>
+#include <AzQtComponents/Components/TitleBarOverdrawHandler.h>
+#include <AzQtComponents/Components/ToolBarArea.h>
+#include <AzQtComponents/Components/ToolButtonComboBox.h>
+#include <AzQtComponents/Components/ToolButtonLineEdit.h>
+#include <AzQtComponents/Components/ToolButtonWithWidget.h>
+#include <AzQtComponents/Components/VectorEdit.h>
+#include <AzQtComponents/Components/WindowDecorationWrapper.h>
+#include <AzQtComponents/Components/Widgets/AssetFolderThumbnailView.h>
+#include <AzQtComponents/Components/Widgets/BreadCrumbs.h>
+#include <AzQtComponents/Components/Widgets/BrowseEdit.h>
+#include <AzQtComponents/Components/Widgets/Card.h>
+#include <AzQtComponents/Components/Widgets/CardHeader.h>
+#include <AzQtComponents/Components/Widgets/CardNotification.h>
+#include <AzQtComponents/Components/Widgets/CheckBox.h>
+#include <AzQtComponents/Components/Widgets/ComboBox.h>
+#include <AzQtComponents/Components/Widgets/DialogButtonBox.h>
+#include <AzQtComponents/Components/Widgets/DragAndDrop.h>
+#include <AzQtComponents/Components/Widgets/ElidingLabel.h>
+#include <AzQtComponents/Components/Widgets/Eyedropper.h>
+#include <AzQtComponents/Components/Widgets/FileDialog.h>
+#include <AzQtComponents/Components/Widgets/LineEdit.h>
+#include <AzQtComponents/Components/Widgets/Menu.h>
+#include <AzQtComponents/Components/Widgets/MessageBox.h>
+#include <AzQtComponents/Components/Widgets/ProgressBar.h>
+#include <AzQtComponents/Components/Widgets/PushButton.h>
+#include <AzQtComponents/Components/Widgets/RadioButton.h>
+#include <AzQtComponents/Components/Widgets/ScrollBar.h>
+#include <AzQtComponents/Components/Widgets/SegmentBar.h>
+#include <AzQtComponents/Components/Widgets/SegmentControl.h>
+#include <AzQtComponents/Components/Widgets/Slider.h>
+#include <AzQtComponents/Components/Widgets/SliderCombo.h>
+#include <AzQtComponents/Components/Widgets/SpinBox.h>
+#include <AzQtComponents/Components/Widgets/StatusBar.h>
+#include <AzQtComponents/Components/Widgets/TabWidget.h>
+#include <AzQtComponents/Components/Widgets/TabWidgetActionToolBar.h>
+#include <AzQtComponents/Components/Widgets/Text.h>
+#include <AzQtComponents/Components/Widgets/ToolBar.h>
+#include <AzQtComponents/Components/Widgets/ToolButton.h>
+#include <AzQtComponents/Components/Widgets/VectorInput.h>

+ 295 - 0
Gems/AzQtComponentsForPython/Code/bindings_azqtcomponents.xml

@@ -0,0 +1,295 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<typesystem package="azqtpyside">
+    <load-typesystem name="typesystem_widgets.xml" generate="no"/>
+    <load-typesystem name="typesystem_core.xml" generate="no"/>
+    <load-typesystem name="typesystem_gui.xml" generate="no"/>
+    
+    <primitive-type name="uint32_t" target-lang-api-name="PyLong"/>
+    
+    <!-- Declaring this prevents addition of broken code by Shiboken.-->
+    <object-type name="QTabWidget" generate="no"/>
+    <object-type name="QSlider" generate="no"/>
+    
+    <object-type name="FlowLayout">
+        <inject-code class="native" position="beginning" file="glue/qtwidgets.cpp" snippet="qlayout-help-functions"/>
+    </object-type>
+     
+     <!-- Remove drawFrame class to prevent broken wrapping in BrowseEdit-->
+    <rejection class="QFrame" function-name="drawFrame"/>
+  
+    <namespace-type name="AzQtComponents" generate="yes">                
+        <!--Components-->
+        <object-type name="ButtonDivider"/>
+        <object-type name="ButtonStripe"/>
+        
+        <namespace-type name="ConfigHelpers" generate="yes">
+            <object-type name="GroupGuard"/>
+            
+            <rejection class="*" function-name="read"/>
+        </namespace-type>
+        
+        <object-type name="DockBar">
+            <enum-type name="DefaultValues"/>
+        </object-type>
+        <object-type name="DockBarButton">
+            <object-type name="Config"/>
+            <enum-type name="WindowDecorationButton"/>
+        </object-type>
+        <object-type name="DockMainWindow"/>
+        <object-type name="DockTabBar"/>
+        <object-type name="DockTabWidget"/>
+        <object-type name="ExtendedLabel"/>   
+
+        <object-type name="FancyDocking">
+            <object-type name="WidgetGrab"/>
+        </object-type>
+        <object-type name="FancyDockingDropZoneWidget"/>
+        <object-type name="FancyDockingGhostWidget"/>
+                
+        <object-type name="FilterCriteriaButton">
+            <enum-type name="ExtraButtonType"/>
+        </object-type>
+        <object-type name="SearchTypeFilter"/>
+        <object-type name="SearchTypeSelector"/>
+        <object-type name="FilteredSearchWidget">
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="GlobalEventFilter"/>
+        <object-type name="HelpButton"/>
+        <object-type name="InteractiveWindowGeometryChanger"/>
+        <object-type name="SearchLineEdit"/>
+        <object-type name="Style">
+            <enum-type name="BorderStyle"/>
+        </object-type>
+        <object-type name="StyledBusyLabel"/>
+        <object-type name="StyledDetailsTableModel">
+            <enum-type name="StyledTableRoles"/>
+            <enum-type name="ColumnStyle"/>
+            <enum-type name="StatusType"/>
+        </object-type>
+        <object-type name="StyledDetailsTableView"/>
+        <object-type name="StyledDialog"/>
+        <object-type name="StyledDockWidget"/>
+        <object-type name="StyledLineEdit">
+            <enum-type name="Flavor"/>
+        </object-type>
+        
+        <object-type name="StyledDoubleSpinBox"/>
+        <object-type name="StyledSpinBox"/>
+        
+        <object-type name="StyleManager">
+            <inject-code class="native" position="beginning">
+                #include &lt;AzCore/IO/Path/Path.h&gt;
+            </inject-code>
+            
+            <add-function signature="initializePath(QString)">
+                <inject-code>
+                    AZStd::string pathString(%1.toUtf8());
+                    AZ::IO::PathView pathView(pathString);
+                    
+                    QApplication* application = static_cast&lt;QApplication*&gt;(QApplication::instance());
+                    cppSelf->initialize(application, pathView);
+                </inject-code>
+            </add-function>
+        </object-type>
+        
+        <object-type name="StyleSheetCache"/>
+        <object-type name="StylesheetPreprocessor"/>
+        <object-type name="TagSelector"/>
+        <object-type name="TitleBar">
+            <object-type name="Config">
+                <object-type name="TitleBar"/>
+                <object-type name="Icon"/>
+                <object-type name="Title"/>
+                <object-type name="Buttons"/>
+            </object-type>
+            <enum-type name="TitleBarDrawMode"/>
+        </object-type>
+        <object-type name="TitleBarOverdrawHandler"/>
+        
+        <object-type name="ToolBarArea"/>
+        <object-type name="ToolButtonComboBox"/>
+        <object-type name="ToolButtonLineEdit"/>
+        <object-type name="ToolButtonWithWidget"/>
+        <object-type name="VectorEdit"/>
+        <object-type name="WindowDecorationWrapper"/>
+
+        <!--Widgets-->   
+        <object-type name="AssetFolderThumbnailView">
+            <object-type name="Config">
+                <object-type name="Thumbnail"/>
+                <object-type name="ExpandButton"/>
+                <object-type name="ChildFrame"/>
+            </object-type>
+            <enum-type name="ThumbnailSize"/>
+        </object-type>
+        
+        <enum-type name="NavigationButton"/>
+        <object-type name="BreadCrumbs">
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="BrowseEdit"/>
+
+        <object-type name="Card">
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="CardHeader">
+            <enum-type name="ContextMenuIcon"/>
+            <value-type name="s_iconSize"/>
+        </object-type>
+        
+        <object-type name="CardNotification"/>
+        <object-type name="CheckBox">
+            <object-type name="Config"/>
+        </object-type>
+                
+        <object-type name="ComboBoxValidator"/>
+        <object-type name="ComboBox">
+            <object-type name="Config"/>
+        </object-type>
+        <object-type name="DialogButtonBox"/>
+        <object-type name="DragAndDrop">
+            <object-type name="DropIndicator"/>
+            <object-type name="DragIndicator"/>
+            <object-type name="Config"/>
+        </object-type>
+        <object-type name="ElidingLabel"/>
+        <object-type name="Eyedropper">
+            <object-type name="Config"/>
+        </object-type>
+        <object-type name="FileDialog">     
+            <modify-function signature="GetSaveFileName(QWidget*,const QString&amp;,const QString&amp;,const QString&amp;,QString*,QFlags&lt;QFileDialog::Option&gt;)" allow-thread="yes">
+                <inject-documentation format="target" mode="append">
+                    This method is works in the same way as the cpp version, with the exception that the selected filter
+                    is returned in a tuple with the selected filename (filename, filter), rather than being stored in the 
+                    filter argument."
+                </inject-documentation>
+                <modify-argument index="return">
+                    <replace-type modified-type="(fileName, selectedFilter)"/>
+                </modify-argument>
+                <modify-argument index="5">
+                    <replace-type modified-type="QString"/>
+                    <replace-default-expression with="QString()"/>
+                </modify-argument>
+                <inject-code class="target" position="end">
+                    //Comment left in to prevent Shiboken including invalid code.
+                    //%RETURN_TYPE retval_ = %CPPSELF.%FUNCTION_NAME(%1, %2, %3, %4, &amp;%5, %6);
+                
+                    %RETURN_TYPE retval_ = %CPPSELF.GetSaveFileName_(%1, %2, %3, %4, &amp;%5, %6);
+                    %PYARG_0 = PyTuple_New(2);
+                    PyTuple_SET_ITEM(%PYARG_0, 0, %CONVERTTOPYTHON[%RETURN_TYPE](retval_));
+                    PyTuple_SET_ITEM(%PYARG_0, 1, %CONVERTTOPYTHON[%ARG5_TYPE](%5));
+
+                </inject-code>
+            </modify-function>
+        </object-type>
+        
+        <rejection class="*" function-name="getBorderColor"/>
+        <rejection class="*" function-name="getBackgroundColor"/>
+        <rejection class="*" function-name="getLineWidth"/>
+        
+        <object-type name="LineEdit">
+            <object-type name="Config"/>
+        </object-type>
+                
+        <object-type name="Menu">
+            <object-type name="Margins"/>
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="AzMessageBox"/>
+        
+        <namespace-type name="Internal" generate="yes"/>
+        <object-type name="OverlayWidgetButton"/>
+        
+        <object-type name="ProgressBar">
+            <object-type name="Config"/>
+        </object-type>
+        
+
+        <object-type name="PushButton">
+            <object-type name="Gradient"/>
+            <object-type name="ColorSet"/>
+            <object-type name="Border"/>
+            <object-type name="Frame"/>
+            <object-type name="SmallIcon"/>
+            <object-type name="IconButton"/>
+            <object-type name="DropdownButton"/>
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="RadioButton">
+            <object-type name="Config"/>
+        </object-type>
+        <object-type name="ScrollBar">
+            <enum-type name="ScrollBarMode"/>
+            <object-type name="Config"/>
+        </object-type>
+        <object-type name="SegmentBar"/>
+        <object-type name="SegmentControl">
+            <enum-type name="TabPosition"/>
+        </object-type>
+        
+        <object-type name="Slider">
+            <object-type name="Border"/>
+            <object-type name="GradientSliderConfig"/>
+            <object-type name="SliderConfig">
+                <object-type name="HandleConfig"/>
+                <object-type name="GrooveConfig"/>
+            </object-type>
+            <object-type name="Config"/>
+        </object-type>
+        <object-type name="SliderInt"/>
+        <object-type name="SliderDouble"/>
+        
+        <object-type name="SliderCombo"/>
+        <object-type name="SliderDoubleCombo"/>
+        
+        <object-type name="SpinBox">
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="StatusBar">
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="TableViewModel">
+            <enum-type name="TableViewModelRole"/>
+        </object-type>
+        <object-type name="TableViewItemDelegate"/>
+         
+        <object-type name="TabWidget">
+            <object-type name="Config"/>
+        </object-type>
+        <object-type name="TabWidgetActionToolBarContainer"/>
+        <object-type name="TabBar"/>
+        
+        <object-type name="TabWidgetActionToolBar"/>
+        
+        <object-type name="Text">
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="ToolBar">
+            <object-type name="ToolBarConfig"/>
+            <object-type name="Config"/>
+            <enum-type name="ToolBarIconSize"/>
+        </object-type>
+        
+        <object-type name="ToolButton">
+            <object-type name="Config"/>
+        </object-type>
+        
+        <object-type name="VectorEditElement">
+            <enum-type name="Flavor"/>
+        </object-type>
+        <object-type name="VectorElement">
+            <enum-type name="Coordinate"/>
+        </object-type>
+        <object-type name="VectorInput"/>
+    </namespace-type> <!--AzQtComponents-->
+</typesystem>

+ 177 - 0
Gems/AzQtComponentsForPython/Code/generated_files.cmake

@@ -0,0 +1,177 @@
+#
+# 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(GENERATED_FILES
+    azqtpyside_module_wrapper.cpp
+    azqtcomponents_wrapper.cpp
+                                                      
+    azqtcomponents_buttondivider_wrapper.cpp
+    azqtcomponents_buttonstripe_wrapper.cpp
+    azqtcomponents_confighelpers_wrapper.cpp
+    azqtcomponents_confighelpers_groupguard_wrapper.cpp
+    azqtcomponents_dockbar_wrapper.cpp
+    azqtcomponents_dockbarbutton_wrapper.cpp
+    azqtcomponents_dockbarbutton_config_wrapper.cpp
+    azqtcomponents_dockmainwindow_wrapper.cpp
+    azqtcomponents_docktabbar_wrapper.cpp
+    azqtcomponents_docktabwidget_wrapper.cpp
+    azqtcomponents_extendedlabel_wrapper.cpp    
+    azqtcomponents_fancydocking_widgetgrab_wrapper.cpp
+    azqtcomponents_fancydocking_wrapper.cpp
+    azqtcomponents_fancydockingdropzonewidget_wrapper.cpp
+    azqtcomponents_fancydockingghostwidget_wrapper.cpp 
+    
+    azqtcomponents_filtercriteriabutton_wrapper.cpp
+    azqtcomponents_searchtypefilter_wrapper.cpp
+    azqtcomponents_searchtypeselector_wrapper.cpp
+    azqtcomponents_filteredsearchwidget_wrapper.cpp
+    azqtcomponents_filteredsearchwidget_config_wrapper.cpp
+
+    
+    flowlayout_wrapper.cpp
+    azqtcomponents_globaleventfilter_wrapper.cpp
+    azqtcomponents_helpbutton_wrapper.cpp
+    azqtcomponents_interactivewindowgeometrychanger_wrapper.cpp
+    azqtcomponents_searchlineedit_wrapper.cpp  
+    azqtcomponents_style_wrapper.cpp
+    azqtcomponents_styledbusylabel_wrapper.cpp
+    azqtcomponents_styleddetailstablemodel_wrapper.cpp
+    azqtcomponents_styleddetailstableview_wrapper.cpp
+    azqtcomponents_styleddialog_wrapper.cpp
+    azqtcomponents_styleddockwidget_wrapper.cpp
+    azqtcomponents_styledlineedit_wrapper.cpp
+    azqtcomponents_styleddoublespinbox_wrapper.cpp
+    azqtcomponents_styledspinbox_wrapper.cpp  
+    azqtcomponents_stylemanager_wrapper.cpp
+    azqtcomponents_stylesheetcache_wrapper.cpp
+    azqtcomponents_stylesheetpreprocessor_wrapper.cpp
+    azqtcomponents_tagselector_wrapper.cpp
+    
+    azqtcomponents_titlebar_wrapper.cpp
+    azqtcomponents_titlebar_config_wrapper.cpp
+    azqtcomponents_titlebar_config_titlebar_wrapper.cpp
+    azqtcomponents_titlebar_config_icon_wrapper.cpp
+    azqtcomponents_titlebar_config_title_wrapper.cpp
+    azqtcomponents_titlebar_config_buttons_wrapper.cpp
+    
+    azqtcomponents_titlebaroverdrawhandler_wrapper.cpp
+#    azqtcomponents_toastnotification_wrapper.cpp
+#    azqtcomponents_toastconfiguration_wrapper.cpp  
+    azqtcomponents_toolbararea_wrapper.cpp
+    azqtcomponents_toolbuttoncombobox_wrapper.cpp
+    azqtcomponents_toolbuttonlineedit_wrapper.cpp
+    azqtcomponents_toolbuttonwithwidget_wrapper.cpp
+    azqtcomponents_vectoredit_wrapper.cpp
+    azqtcomponents_windowdecorationwrapper_wrapper.cpp   
+    
+ #   azqtcomponents_assetfolderlistview_wrapper.cpp
+    azqtcomponents_assetfolderthumbnailview_wrapper.cpp
+    azqtcomponents_assetfolderthumbnailview_config_wrapper.cpp
+    azqtcomponents_assetfolderthumbnailview_config_thumbnail_wrapper.cpp
+    azqtcomponents_assetfolderthumbnailview_config_expandbutton_wrapper.cpp
+    azqtcomponents_assetfolderthumbnailview_config_childframe_wrapper.cpp
+    azqtcomponents_breadcrumbs_wrapper.cpp
+    azqtcomponents_breadcrumbs_config_wrapper.cpp
+    azqtcomponents_browseedit_wrapper.cpp
+    azqtcomponents_card_wrapper.cpp
+    azqtcomponents_card_config_wrapper.cpp
+    azqtcomponents_cardheader_wrapper.cpp
+    azqtcomponents_cardnotification_wrapper.cpp
+    azqtcomponents_checkbox_wrapper.cpp
+    azqtcomponents_checkbox_config_wrapper.cpp
+ #   azqtcomponents_colorlabel_wrapper.cpp
+    azqtcomponents_comboboxvalidator_wrapper.cpp
+    azqtcomponents_combobox_wrapper.cpp
+    azqtcomponents_combobox_config_wrapper.cpp
+    azqtcomponents_dialogbuttonbox_wrapper.cpp
+    
+    azqtcomponents_draganddrop_wrapper.cpp
+    azqtcomponents_draganddrop_dropindicator_wrapper.cpp
+    azqtcomponents_draganddrop_dragindicator_wrapper.cpp
+    azqtcomponents_draganddrop_config_wrapper.cpp
+    
+    azqtcomponents_elidinglabel_wrapper.cpp
+    azqtcomponents_eyedropper_wrapper.cpp
+    azqtcomponents_eyedropper_config_wrapper.cpp
+    azqtcomponents_filedialog_wrapper.cpp
+ #   azqtcomponents_gradientslider_wrapper.cpp
+    azqtcomponents_lineedit_wrapper.cpp
+    azqtcomponents_lineedit_config_wrapper.cpp
+    
+    azqtcomponents_menu_wrapper.cpp
+    azqtcomponents_menu_margins_wrapper.cpp
+    azqtcomponents_menu_config_wrapper.cpp
+    
+    azqtcomponents_azmessagebox_wrapper.cpp
+ #   azqtcomponents_overlaywidgetbutton_wrapper.cpp
+ #   azqtcomponents_overlaywidget_wrapper.cpp
+    azqtcomponents_progressbar_wrapper.cpp
+    azqtcomponents_progressbar_config_wrapper.cpp
+    
+    azqtcomponents_pushbutton_wrapper.cpp
+    azqtcomponents_pushbutton_gradient_wrapper.cpp
+    azqtcomponents_pushbutton_colorset_wrapper.cpp
+    azqtcomponents_pushbutton_border_wrapper.cpp
+    azqtcomponents_pushbutton_frame_wrapper.cpp
+    azqtcomponents_pushbutton_smallicon_wrapper.cpp
+    azqtcomponents_pushbutton_iconbutton_wrapper.cpp
+    azqtcomponents_pushbutton_dropdownbutton_wrapper.cpp
+    azqtcomponents_pushbutton_config_wrapper.cpp
+    
+    azqtcomponents_radiobutton_wrapper.cpp
+    azqtcomponents_radiobutton_config_wrapper.cpp
+    azqtcomponents_scrollbar_wrapper.cpp
+    azqtcomponents_scrollbar_config_wrapper.cpp
+    azqtcomponents_segmentbar_wrapper.cpp
+    azqtcomponents_segmentcontrol_wrapper.cpp
+    
+    azqtcomponents_slider_wrapper.cpp
+    azqtcomponents_slider_border_wrapper.cpp
+    azqtcomponents_slider_gradientsliderconfig_wrapper.cpp
+    azqtcomponents_slider_sliderconfig_wrapper.cpp
+    azqtcomponents_slider_sliderconfig_handleconfig_wrapper.cpp
+    azqtcomponents_slider_sliderconfig_grooveconfig_wrapper.cpp
+    azqtcomponents_slider_config_wrapper.cpp
+    azqtcomponents_sliderint_wrapper.cpp
+    azqtcomponents_sliderdouble_wrapper.cpp
+    azqtcomponents_slidercombo_wrapper.cpp
+    azqtcomponents_sliderdoublecombo_wrapper.cpp
+    
+    azqtcomponents_spinbox_wrapper.cpp
+    azqtcomponents_spinbox_config_wrapper.cpp
+    azqtcomponents_statusbar_wrapper.cpp
+    azqtcomponents_statusbar_config_wrapper.cpp
+ #   azqtcomponents_tableview_wrapper.cpp
+ #   azqtcomponents_tableview_config_wrapper.cpp
+  #  azqtcomponents_tableviewmodel_wrapper.cpp
+ #   azqtcomponents_tableviewitemdelegate_wrapper.cpp
+    azqtcomponents_tabwidgetactiontoolbarcontainer_wrapper.cpp
+    azqtcomponents_tabwidget_wrapper.cpp
+    azqtcomponents_tabwidget_config_wrapper.cpp
+    azqtcomponents_tabwidgetactiontoolbar_wrapper.cpp
+    azqtcomponents_tabbar_wrapper.cpp
+    
+    azqtcomponents_text_wrapper.cpp
+    azqtcomponents_text_config_wrapper.cpp
+    azqtcomponents_toolbar_wrapper.cpp
+    azqtcomponents_toolbar_toolbarconfig_wrapper.cpp
+    azqtcomponents_toolbar_config_wrapper.cpp
+    
+    azqtcomponents_toolbutton_wrapper.cpp
+    azqtcomponents_toolbutton_config_wrapper.cpp
+ #   azqtcomponents_treeview_wrapper.cpp
+ #   azqtcomponents_treeview_config_wrapper.cpp
+ #   azqtcomponents_branchdelegate_wrapper.cpp
+ #   azqtcomponents_styledtreeview_wrapper.cpp
+ #   azqtcomponents_styledtreewidget_wrapper.cpp
+    azqtcomponents_vectoreditelement_wrapper.cpp
+    azqtcomponents_vectorelement_wrapper.cpp
+    azqtcomponents_vectorinput_wrapper.cpp
+    
+    azqtcomponents_internal_wrapper.cpp
+)

+ 8 - 0
Gems/AzQtComponentsForPython/Code/license_header.txt

@@ -0,0 +1,8 @@
+/*
+ * 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
+ *
+ */
+ 

+ 23 - 0
Gems/AzQtComponentsForPython/gem.json

@@ -0,0 +1,23 @@
+{
+    "gem_name": "AzQtComponentsForPython",
+    "display_name": "AzQtComponents For Python",
+    "license": "Apache-2.0 Or MIT",
+    "license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt",
+    "origin": "Open 3D Engine - o3de.org",
+    "origin_url": "https://github.com/o3de/o3de",
+    "type": "Code",
+    "summary": "This gem provides a library to use the AzQtComponents library from Python.",
+    "canonical_tags": [
+        "Gem"
+    ],
+    "user_tags": [
+        "Scripting",
+        "UI"
+    ],
+    "icon_path": "preview.png",
+    "requirements": "",
+    "documentation_url": "",
+    "dependencies": [
+        "EditorPythonBindings"
+    ]
+}

+ 3 - 0
Gems/AzQtComponentsForPython/preview.png

@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:de0e6e480ece5b423222f4feacf56553d73713fe9afea8bbc9a2660a3cd54ec7
+size 1232

+ 9 - 1
Gems/OpenXRVk/3rdParty/Platform/Android/BuiltInPackages_android.cmake

@@ -6,4 +6,12 @@
 #
 #
 
-ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev1-android    TARGETS OpenXR  PACKAGE_HASH 1227204583ce224c7e3843e82bb36deb576df6b458eecce46740cb8941902f21)
+set(ANDROID_USE_OCULUS_OPENXR OFF CACHE BOOL "When ON it uses OpenXR library from Oculus SDK.")
+
+if(ANDROID_USE_OCULUS_OPENXR)
+    include(${CMAKE_CURRENT_LIST_DIR}/FindOpenXROculus.cmake)
+    set(openxr_dependency 3rdParty::OpenXROculus)
+else()
+    ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev1-android    TARGETS OpenXR  PACKAGE_HASH 1227204583ce224c7e3843e82bb36deb576df6b458eecce46740cb8941902f21)
+    set(openxr_dependency 3rdParty::OpenXR)
+endif()

+ 42 - 0
Gems/OpenXRVk/3rdParty/Platform/Android/FindOpenXROculus.cmake

@@ -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
+#
+#
+
+# this file actually ingests the library and defines targets.
+set(TARGET_WITH_NAMESPACE "3rdParty::OpenXROculus")
+if (TARGET ${TARGET_WITH_NAMESPACE})
+    return()
+endif()
+
+set(MY_NAME "OpenXROculus")
+
+get_property(openxrvk_gem_root GLOBAL PROPERTY "@GEMROOT:OpenXRVk@")
+
+set(OculusOpenXRSDKPath ${openxrvk_gem_root}/External/OculusOpenXRMobileSDK)
+
+set(${MY_NAME}_INCLUDE_DIR 
+    ${OculusOpenXRSDKPath}/3rdParty/khronos/openxr/OpenXR-SDK/include
+    ${OculusOpenXRSDKPath}/OpenXR/Include)
+
+set(PATH_TO_SHARED_LIBS ${OculusOpenXRSDKPath}/OpenXR/Libs/Android/arm64-v8a)
+
+if(NOT EXISTS ${PATH_TO_SHARED_LIBS}/Release/libopenxr_loader.so)
+    message(FATAL_ERROR
+        "Oculus OpenXR loader library not found at ${PATH_TO_SHARED_LIBS}/Release. "
+        "Oculus OpenXR Mobile SDK needs to be downloaded via https://developer.oculus.com/downloads/native-android/ "
+        "and uncompressed into OpenXRVk/External/OculusOpenXRMobileSDK folder.")
+    return()
+endif()
+
+add_library(${TARGET_WITH_NAMESPACE} SHARED IMPORTED GLOBAL)
+ly_target_include_system_directories(TARGET ${TARGET_WITH_NAMESPACE} INTERFACE ${${MY_NAME}_INCLUDE_DIR})
+set_target_properties(${TARGET_WITH_NAMESPACE}
+    PROPERTIES
+        IMPORTED_LOCATION ${PATH_TO_SHARED_LIBS}/Release/libopenxr_loader.so
+        IMPORTED_LOCATION_DEBUG ${PATH_TO_SHARED_LIBS}/Debug/libopenxr_loader.so)
+
+set(${MY_NAME}_FOUND True)

+ 2 - 0
Gems/OpenXRVk/3rdParty/Platform/Linux/BuiltInPackages_linux.cmake

@@ -7,3 +7,5 @@
 #
 
 ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev2-linux  TARGETS OpenXR  PACKAGE_HASH 7d9045de0078a3f4a88bea2e3167e2c159acc8c62ac40ae15f8a31902b8d1f08)
+
+set(openxr_dependency 3rdParty::OpenXR)

+ 2 - 0
Gems/OpenXRVk/3rdParty/Platform/Windows/BuiltInPackages_windows.cmake

@@ -7,3 +7,5 @@
 #
 
 ly_associate_package(PACKAGE_NAME OpenXR-1.0.22-rev1-windows    TARGETS OpenXR  PACKAGE_HASH 55235d77253efe1af046a4a3e7dd7a8e5f6768401326d5e077c827cce323cd11)
+
+set(openxr_dependency 3rdParty::OpenXR)

+ 37 - 3
Gems/OpenXRVk/Code/CMakeLists.txt

@@ -55,7 +55,7 @@ ly_add_target(
         PUBLIC
             AZ::AzCore
             AZ::AzFramework
-            3rdParty::OpenXR
+            ${openxr_dependency}
             AZ::AtomCore
             Gem::Atom_RHI_Vulkan.Reflect
             Gem::Atom_RHI_Vulkan.Glad.Static
@@ -79,6 +79,40 @@ ly_add_target(
             Gem::OpenXRVk.Static
 )
 
-# use the OpenXRVk module in clients and tools:
+# use the OpenXRVk module in all aliases:
 ly_create_alias(NAME OpenXRVk.Clients NAMESPACE Gem TARGETS Gem::OpenXRVk)
-ly_create_alias(NAME OpenXRVk.Tools 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)
+endif()
+
+################################################################################
+# Tests
+################################################################################
+if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
+
+    ly_add_target(
+        NAME OpenXRVk.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+        NAMESPACE Gem
+        OUTPUT_NAME OpenXRVk.Tests
+        FILES_CMAKE
+            openxrvk_tests_files.cmake
+            ${pal_source_dir}/platform_test_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Tests
+                .
+                Source
+                ${pal_source_dir}
+        BUILD_DEPENDENCIES
+            PRIVATE
+                AZ::AzTestShared
+                AZ::AzTest
+                Gem::OpenXRVk.Static
+    )
+    ly_add_googletest(
+        NAME Gem::OpenXRVk.Tests
+    )
+
+endif()

+ 468 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/InputDeviceXRController.h

@@ -0,0 +1,468 @@
+/*
+ * 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 <AzFramework/Input/Buses/Requests/InputHapticFeedbackRequestBus.h>
+#include <AzFramework/Input/Channels/InputChannelAnalog.h>
+#include <AzFramework/Input/Channels/InputChannelAxis1D.h>
+#include <AzFramework/Input/Channels/InputChannelAxis2D.h>
+#include <AzFramework/Input/Channels/InputChannelAxis3D.h>
+#include <AzFramework/Input/Channels/InputChannelDigital.h>
+#include <AzFramework/Input/Channels/InputChannelQuaternion.h>
+#include <AzFramework/Input/Devices/InputDevice.h>
+
+#include <AzFramework/Entity/EntityDebugDisplayBus.h>
+
+
+namespace AzFramework
+{
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //! Defines a generic XR controller pair device, including the ids of all associated input
+    //! channels. Platform specifics are defined as private implementations so that creating an
+    //! instance of this generic class will work correctly on any platform supporting this type of
+    //! hand-held XR controllers.
+    class InputDeviceXRController
+        : public InputDevice
+        , public InputHapticFeedbackRequestBus::Handler
+        , public AzFramework::DebugDisplayEventBus::Handler
+    {
+    public:
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! The name used to identify an XR Controller input device
+        static constexpr const char* Name{ "xr_controller" };
+        static constexpr InputDeviceId IdForIndex0{ Name, 0 };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! Check whether an input device id identifies an XR controller (regardless of index)
+        //! @param inputDeviceId The input device id to check
+        //! @return True if the input device id identifies as an XR controller, False otherwise
+        static bool IsXRControllerDevice(const InputDeviceId& inputDeviceId);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! All the input channel ids that identify XR Controller digital button inputs
+        struct Button
+        {
+            static constexpr InputChannelId A{ "xr_controller_button_a" }; //!< The right-hand A button
+            static constexpr InputChannelId B{ "xr_controller_button_b" }; //!< The right-hand B button
+            static constexpr InputChannelId X{ "xr_controller_button_x" }; //!< The left-hand X button
+            static constexpr InputChannelId Y{ "xr_controller_button_y" }; //!< The left-hand Y button
+            static constexpr InputChannelId Home{ "xr_controller_button_home" }; //!< The right-hand "Home" button
+            static constexpr InputChannelId Menu{ "xr_controller_button_menu" }; //!< The left-hand "Menu" button
+            static constexpr InputChannelId L3{ "xr_controller_button_l3" }; //!< The left-hand thumb-stick click button
+            static constexpr InputChannelId R3{ "xr_controller_button_r3" }; //!< The right-hand thumb-stick click button
+
+            static constexpr InputChannelId TA{ "xr_controller_touch_button_a" }; //!< The A button touch detection
+            static constexpr InputChannelId TB{ "xr_controller_touch_button_b" }; //!< The B button touch detection
+            static constexpr InputChannelId TX{ "xr_controller_touch_button_x" }; //!< The X button touch detection
+            static constexpr InputChannelId TY{ "xr_controller_touch_button_y" }; //!< The Y button touch detection
+            static constexpr InputChannelId TLStick{ "xr_controller_touch_thumbstick_l" }; //!< The left thumb-stick touch detection
+            static constexpr InputChannelId TRStick{ "xr_controller_touch_thumbstick_r" }; //!< The right thumb-stick touch detection
+            static constexpr InputChannelId TLRest{ "xr_controller_touch_thumbrest_l" }; //!< The left thumb-rest touch detection
+            static constexpr InputChannelId TRRest{ "xr_controller_touch_thumbrest_r" }; //!< The right thumb-rest touch detection
+            static constexpr InputChannelId TLTrig{ "xr_controller_touch_trigger_l" }; //!< The left trigger touch detection
+            static constexpr InputChannelId TRTrig{ "xr_controller_touch_trigger_r" }; //!< The right trigger touch detection
+
+            //! All digital XR controller button ids
+            static constexpr AZStd::array All
+            {
+                A,
+                B,
+                X,
+                Y,
+                Home,
+                Menu,
+                L3,
+                R3,
+                TA,
+                TB,
+                TX,
+                TY,
+                TLStick,
+                TRStick,
+                TLRest,
+                TRRest,
+                TLTrig,
+                TRTrig,
+            };
+        };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! All the input channel ids that identify XR Controller analog inputs
+        struct Trigger
+        {
+            static constexpr InputChannelId LTrigger{ "xr_controller_trigger_l" }; //!< The left-hand trigger
+            static constexpr InputChannelId RTrigger{ "xr_controller_trigger_r" }; //!< The right-hand trigger
+            static constexpr InputChannelId LGrip{ "xr_controller_grip_l" }; //!< The left-hand grip
+            static constexpr InputChannelId RGrip{ "xr_controller_grip_r" }; //!< The right-hand grip
+
+            //! All analog XR Controller input ids
+            static constexpr AZStd::array All
+            {
+                LTrigger,
+                RTrigger,
+                LGrip,
+                RGrip
+            };
+        };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! All the input channel ids that identify XR Controller 1D axis inputs
+        struct ThumbStickAxis1D
+        {
+            static constexpr InputChannelId LX{ "xr_controller_thumbstick_l_x" }; //!< X-axis of the left-hand thumb-stick
+            static constexpr InputChannelId LY{ "xr_controller_thumbstick_l_y" }; //!< Y-axis of the left-hand thumb-stick
+            static constexpr InputChannelId RX{ "xr_controller_thumbstick_r_x" }; //!< X-axis of the right-hand thumb-stick
+            static constexpr InputChannelId RY{ "xr_controller_thumbstick_r_y" }; //!< Y-axis of the right-hand thumb-stick
+
+            //! All 1D axis XR Controller input ids
+            static constexpr AZStd::array All
+            {
+                LX,
+                LY,
+                RX,
+                RY
+            };
+        };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! All the input channel ids that identify XR Controller 2D axis inputs
+        struct ThumbStickAxis2D
+        {
+            static constexpr InputChannelId L{ "xr_controller_thumbstick_l" }; //!< The left-hand thumb-stick
+            static constexpr InputChannelId R{ "xr_controller_thumbstick_r" }; //!< The right-hand thumb-stick
+
+            //! All 2D axis XR Controller input ids
+            static constexpr AZStd::array All
+            {
+                L,
+                R
+            };
+        };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! All the input channel ids that identify XR Controller thumb-stick directions
+        struct ThumbStickDirection
+        {
+            static constexpr InputChannelId LU{ "xr_controller_thumbstick_l_up" }; //!< Up on the left-hand thumb-stick
+            static constexpr InputChannelId LD{ "xr_controller_thumbstick_l_down" }; //!< Down on the left-hand thumb-stick
+            static constexpr InputChannelId LL{ "xr_controller_thumbstick_l_left" }; //!< Left on the left-hand thumb-stick
+            static constexpr InputChannelId LR{ "xr_controller_thumbstick_l_right" }; //!< Right on the left-hand thumb-stick
+            static constexpr InputChannelId RU{ "xr_controller_thumbstick_r_up" }; //!< Up on the right-hand thumb-stick
+            static constexpr InputChannelId RD{ "xr_controller_thumbstick_r_down" }; //!< Down on the right-hand thumb-stick
+            static constexpr InputChannelId RL{ "xr_controller_thumbstick_r_left" }; //!< Left on the right-hand thumb-stick
+            static constexpr InputChannelId RR{ "xr_controller_thumbstick_r_right" }; //!< Right on the right-hand thumb-stick
+
+            //! All thumb-stick directional XR Controller input ids
+            static constexpr AZStd::array All
+            {
+                LU,
+                LD,
+                LL,
+                LR,
+                RU,
+                RD,
+                RL,
+                RR
+            };
+        };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! All the input channel ids that identify XR Controller 3D axis inputs
+        struct ControllerPosePosition
+        {
+            static constexpr InputChannelId LPos{ "xr_controller_position_l" }; //!< The left-hand position
+            static constexpr InputChannelId RPos{ "xr_controller_position_r" }; //!< The right-hand position
+
+            //! All XR Controller position input ids
+            static constexpr AZStd::array All
+            {
+                LPos,
+                RPos,
+            };
+        };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! All the input channel ids that identify XR Controller orientation inputs
+        struct ControllerPoseOrientation
+        {
+            static constexpr InputChannelId LOrient{ "xr_controller_orientation_l" }; //!< The left-hand orientation
+            static constexpr InputChannelId ROrient{ "xr_controller_orientation_r" }; //!< The right-hand orientation
+
+            //! All XR Controller orientation input ids
+            static constexpr AZStd::array All
+            {
+                LOrient,
+                ROrient
+            };
+        };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Allocator
+        AZ_CLASS_ALLOCATOR(InputDeviceXRController, AZ::SystemAllocator, 0);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Type Info
+        AZ_RTTI(InputDeviceXRController, "{31FC6155-5902-46E3-9CB7-C7E7673FE4CC}", InputDevice);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Reflection
+        static void Reflect(AZ::ReflectContext* context);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // The internal implementation class that is passed to the constructor
+        class Implementation
+        {
+        public:
+            AZ_CLASS_ALLOCATOR(Implementation, AZ::SystemAllocator, 0);
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Default factory create function
+            //! @param inputDevice Reference to the input device being implemented
+            static Implementation* Create(InputDeviceXRController& inputDevice);
+
+            Implementation(InputDeviceXRController& inputDevice);
+            AZ_DISABLE_COPY_MOVE(Implementation);
+            virtual ~Implementation() = default;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Query for a path representing an input channel
+            //! Used for initializing Xr inputs.
+            virtual AZStd::string GetInputChannelPath(const InputChannelId& channelId) const = 0;
+
+            virtual AZStd::string GetInputDeviceProfilePath() const = 0;
+            virtual AZStd::string GetLeftHandSubPath() const = 0;
+            virtual AZStd::string GetRightHandSubPath() const = 0;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Register a callback function for this implementation to call
+            //! This callback is called during tick updates and allows for platform code to run
+            //! updates to refresh state of the controller data.
+            //! @param callbackFn The callback function
+            using TickCallbackFn = AZStd::function<void()>;
+            virtual void RegisterTickCallback(TickCallbackFn callbackFn) = 0;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Query the connected state of the device
+            //! @return True if the input device is currently connected, False otherwise
+            virtual bool IsConnected() const = 0;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Set the current vibration speed of the motors
+            //! @param leftMotorSpeedNormalized Speed of the left motor
+            //! @param rightMotorSpeedNormalized Speed of the right motor
+            virtual void SetVibration(float leftMotorSpeedNormalized,
+                                      float rightMotorSpeedNormalized) = 0;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Tick/update the input device to broadcast all input events since the last frame
+            virtual void TickInputDevice() = 0;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Broadcast an event when the input device connects to the system
+            void BroadcastInputDeviceConnectedEvent() const;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Broadcast an event when the input device disconnects from the system
+            void BroadcastInputDeviceDisconnectedEvent() const;
+
+            using ButtonIdToBitMaskMap = AZStd::unordered_map<InputChannelId, AZ::u32>;
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Platform agnostic representation of raw XR Controller state
+            struct RawXRControllerState
+            {
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Constructor
+                //! @param digitalButtonMap A map of digital button ids by bitmask
+                explicit RawXRControllerState(ButtonIdToBitMaskMap digitalButtonMap);
+
+                AZ_DISABLE_COPY_MOVE(RawXRControllerState);
+                ~RawXRControllerState() = default;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Reset the raw xr controller data
+                void Reset();
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Gets a digital button's current state
+                bool GetDigitalButtonState(const InputChannelId& channelId) const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the left trigger value adjusted for the dead zone and normalized
+                //! @return The adjusted left trigger value
+                float GetLeftTriggerAdjustedForDeadZoneAndNormalized() const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the right trigger value adjusted for the dead zone and normalized
+                //! @return The adjusted right trigger value
+                float GetRightTriggerAdjustedForDeadZoneAndNormalized() const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the left grip value adjusted for the dead zone and normalized
+                //! @return The adjusted left grip value
+                float GetLeftGripAdjustedForDeadZoneAndNormalized() const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the right grip value adjusted for the dead zone and normalized
+                //! @return The adjusted right grip value
+                float GetRightGripAdjustedForDeadZoneAndNormalized() const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the left thumb-stick values adjusted for the dead zone and normalized
+                //! @return The adjusted left thumb-stick values
+                AZ::Vector2 GetLeftThumbStickAdjustedForDeadZoneAndNormalized() const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the right thumb-stick values adjusted for the dead zone and normalized
+                //! @return The adjusted right thumb-stick values
+                AZ::Vector2 GetRightThumbStickAdjustedForDeadZoneAndNormalized() const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the left thumb-stick values normalized with no dead zone applied
+                //! @return The normalized left thumb-stick values
+                AZ::Vector2 GetLeftThumbStickNormalizedValues() const;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                //! Get the right thumb-stick values normalized with no dead zone applied
+                //! @return The normalized right thumb-stick values
+                AZ::Vector2 GetRightThumbStickNormalizedValues() const;
+
+                const ButtonIdToBitMaskMap m_buttonIdsToBitMasks;
+
+                ////////////////////////////////////////////////////////////////////////////////////
+                // Raw Data
+                AZ::u32 m_digitalButtonStates = 0;          //!< The state of all digital buttons
+                float m_leftTriggerState = 0.f;             //!< The left trigger value
+                float m_rightTriggerState = 0.f;            //!< The right trigger value
+                float m_leftGripState = 0.f;                //!< The left grip value
+                float m_rightGripState = 0.f;               //!< The right grip value
+                float m_leftThumbStickXState = 0.f;         //!< The left thumb-stick x-axis
+                float m_leftThumbStickYState = 0.f;         //!< The left thumb-stick y-axis
+                float m_rightThumbStickXState = 0.f;        //!< The right thumb-stick x-axis
+                float m_rightThumbStickYState = 0.f;        //!< The right thumb-stick y-axis
+
+                float m_triggerMaxValue = 0.f;              //!< The maximum value of the analog triggers
+                float m_triggerDeadZoneValue = 0.f;         //!< The dead zone value of the analog triggers
+                float m_gripMaxValue = 0.f;                 //!< The maximum value of the grip triggers
+                float m_gripDeadZoneValue = 0.f;            //!< The dead zone value of the grip triggers
+                float m_thumbStickMaxValue = 0.f;           //!< The maximum value of the thumb-sticks
+                float m_leftThumbStickDeadZoneValue = 0.f;  //!< The left thumb-stick dead zone value
+                float m_rightThumbStickDeadZoneValue = 0.f; //!< The right thumb-stick dead zone value
+
+                float m_leftMotorVibrationValue = 0.f;      //!< The vibration amount of the left motor
+                float m_rightMotorVibrationValue = 0.f;     //!< The vibration amount of the right motor
+
+                AZ::Vector3 m_leftPositionState = AZ::Vector3::CreateZero();                //!< The left controller position
+                AZ::Vector3 m_rightPositionState = AZ::Vector3::CreateZero();               //!< The right controller position
+                AZ::Quaternion m_leftOrientationState = AZ::Quaternion::CreateIdentity();   //!< The left controller orientation
+                AZ::Quaternion m_rightOrientationState = AZ::Quaternion::CreateIdentity();  //!< The right controller orientation
+            }; // struct RawXRControllerState
+
+            virtual RawXRControllerState& GetRawState() = 0;
+
+        protected:
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Process a controller state that has been obtained since the last call to this function.
+            //! @param rawControllerState The raw controller state
+            void ProcessRawControllerState(const RawXRControllerState& rawControllerState);
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! Reset the state of all this input device's associated input channels
+            void ResetInputChannelStates();
+
+            ////////////////////////////////////////////////////////////////////////////////////////
+            //! @see AzFramework::InputDeviceId::GetIndex
+            AZ::u32 GetInputDeviceIndex() const;
+
+        private:
+            InputDeviceXRController& m_inputDevice;
+        }; // class Implementation
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Alias for the function type used to create a custom implementation for this input device
+        using ImplementationFactory = AZStd::function<Implementation*(InputDeviceXRController&)>;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        InputDeviceXRController();
+        explicit InputDeviceXRController(const InputDeviceId& inputDeviceId,
+                                         ImplementationFactory implFactoryFn = &Implementation::Create);
+        AZ_DISABLE_COPY_MOVE(InputDeviceXRController);
+        ~InputDeviceXRController() override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // AzFramework::InputDevice interface
+        const InputChannelByIdMap& GetInputChannelsById() const override;
+        bool IsSupported() const override;
+        bool IsConnected() const override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // AzFramework::InputDeviceRequests interface
+        void TickInputDevice() override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // AzFramework::InputHapticFeedbackRequests interface
+        void SetVibration(float leftMotorSpeedNormalized, float rightMotorSpeedNormalized) override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! Set the implementation of this input device
+        //! @param impl The Implementation to use
+        void SetImplementation(AZStd::unique_ptr<Implementation> impl);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! Set the implementation of this input device
+        //! @param implFactoryFn The Implementation factory create function to use
+        void SetImplementation(const ImplementationFactory& implFactoryFn);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! Get the non-owning pointer to the implementation of this input device
+        //! @return The raw implementation pointer
+        Implementation* GetImplementation() const;
+
+    protected:
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // AzFramework::DebugDisplayEventBus interface
+        void DrawGlobalDebugInfo() override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! Helper routine that checks for a specific controller input combo and will toggle the debug draw.
+        //! Allows users to quickly display/hide the debug information for the xr controllers without having
+        //! to type anything into a console.
+        //! Currently bound to: Button::Menu + Trigger::LTrigger
+        //! That is, hold the left controller's Menu button and squeeze the left trigger fully.
+        void CheckDebugDrawCheat() const;
+
+        static constexpr float s_thumbStickMaxValue{ 1.f };
+        static constexpr float s_thumbStickMinValue{ -1.f };
+        static constexpr float s_thumbStickCenterValue{ 0.f };
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        using ButtonChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelDigital*>;
+        using TriggerChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAnalog*>;
+        using ThumbStickAxis1DChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis1D*>;
+        using ThumbStickAxis2DChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis2D*>;
+        using ThumbStickDirectionChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAnalog*>;
+        using ControllerAxis3DChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelAxis3D*>;
+        using ControllerPoseChannelByIdMap = AZStd::unordered_map<InputChannelId, InputChannelQuaternion*>;
+
+        InputChannelByIdMap m_allChannelsById{}; //!< All XR Controller input channels by id
+        ButtonChannelByIdMap m_buttonChannelsById{}; //!< All digital button channels by id
+        TriggerChannelByIdMap m_triggerChannelsById{}; //!< All analog trigger channels by id
+        ThumbStickAxis1DChannelByIdMap m_thumbStick1DChannelsById{}; //!< All thumb-stick 1D axis channels by id
+        ThumbStickAxis2DChannelByIdMap m_thumbStick2DChannelsById{}; //!< All thumb-stick 2D axis channels by id
+        ThumbStickDirectionChannelByIdMap m_thumbStickDirectionChannelsById{}; //!< All thumb-stick direction channels by id
+        ControllerAxis3DChannelByIdMap m_controllerPositionChannelsById{}; //!< All controller position channels by id
+        ControllerPoseChannelByIdMap m_controllerOrientationChannelsById{}; //!< All controller orientation channels by id
+
+    private:
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! Private pointer to the platform implementation
+        AZStd::unique_ptr<Implementation> m_impl;
+    };
+
+} // namespace AzFramework

+ 5 - 3
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkDevice.h

@@ -30,9 +30,9 @@ namespace OpenXRVk
         // Create the xr specific native device object and populate the XRDeviceDescriptor with it.
         AZ::RHI::ResultCode InitDeviceInternal(AZ::RHI::XRDeviceDescriptor* instanceDescriptor) override;
         //! Get the Fov data  of the view specified by view index
-        AZ::RPI::FovData GetViewFov(AZ::u32 viewIndex) const override;
+        AZ::RHI::ResultCode GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const override;
         //! Get the Pose data  of the view specified by view index
-        AZ::RPI::PoseData GetViewPose(AZ::u32 viewIndex) const override;
+        AZ::RHI::ResultCode GetViewPose(AZ::u32 viewIndex, AZ::RPI::PoseData& outPoseData) const override;
         //////////////////////////////////////////////////////////////////////////
 
         //! Returns true if rendering data is valid for the current frame.
@@ -60,6 +60,8 @@ namespace OpenXRVk
         bool BeginFrameInternal() override;
         //! Release the oldest swapchain image and inform the drivers that the frame is ending 
         void EndFrameInternal(XR::Ptr<XR::SwapChain>) override;
+        //! Called after EndFrame has been executed.
+        void PostFrameInternal() override;
         //! Locate views, acquire swapchain image and synchronize gpu with cpu
         bool AcquireSwapChainImageInternal(AZ::u32 viewIndex, XR::SwapChain* baseSwapChain) override;
         //////////////////////////////////////////////////////////////////////////
@@ -71,6 +73,6 @@ namespace OpenXRVk
         AZStd::vector<XrCompositionLayerProjectionView> m_projectionLayerViews;
         AZStd::vector<XrView> m_views;
         uint32_t m_viewCountOutput = 0;
-        GladVulkanContext m_context;
+        GladVulkanContext m_context = {};
     };
 }

+ 66 - 26
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInput.h

@@ -9,7 +9,10 @@
 #pragma once
 
 #include <XR/XRInput.h>
+#include <OpenXRVk/InputDeviceXRController.h>
+#include <OpenXRVk/OpenXRVkSpace.h>
 #include <OpenXRVk_Platform.h>
+#include <Atom/RPI.Public/XR/XRRenderingInterface.h>
 
 namespace OpenXRVk
 {
@@ -22,10 +25,10 @@ namespace OpenXRVk
         AZ_RTTI(Input, "{97ADD1FE-27DF-4F36-9F61-683F881F9477}", XR::Input);
 
         static XR::Ptr<Input> Create();
-    
+
         //! Sync all the actions and update controller
-        //! as well as various tracked space poses 
-        void PollActions() override;
+        //! as well as various tracked space poses
+        void PollActions();
 
         //! Initialize various actions/actions sets and add support for Oculus touch bindings
         AZ::RHI::ResultCode InitInternal() override;
@@ -34,7 +37,7 @@ namespace OpenXRVk
         AZ::RHI::ResultCode InitializeActionSpace(XrSession xrSession);
 
         //! Attach action sets
-        AZ::RHI::ResultCode InitializeActionSets(XrSession xrSession);
+        AZ::RHI::ResultCode InitializeActionSets(XrSession xrSession) const;
 
         //! Update Controller space information
         void LocateControllerSpace(XrTime predictedDisplayTime, XrSpace baseSpace, AZ::u32 handIndex);
@@ -42,47 +45,84 @@ namespace OpenXRVk
         //! Update information for a specific tracked space type (i.e visualizedSpaceType)
         void LocateVisualizedSpace(XrTime predictedDisplayTime, XrSpace space, XrSpace baseSpace, OpenXRVk::SpaceType visualizedSpaceType);
 
-        //! Return Pose data for a controller attached to a view index
-        AZ::RPI::PoseData GetControllerPose(AZ::u32 viewIndex) const;
+        //! Return Pose data for a controller attached to a hand index
+        AZ::RHI::ResultCode GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const;
 
-        //! Return scale for a controller attached to a view index
-        float GetControllerScale(AZ::u32 viewIndex) const;
+        //! Return scale for a controller attached to a hand index
+        float GetControllerScale(AZ::u32 handIndex) const;
 
         //! Return Pose data for a tracked space type (i.e visualizedSpaceType)
-        AZ::RPI::PoseData GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType) const;
+        AZ::RHI::ResultCode GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType, AZ::RPI::PoseData& outPoseData) const;
 
-        //! Get the Grab action
-        XrAction GetGrabAction() const;
+        //! Get the Squeeze action
+        XrAction GetSqueezeAction(AZ::u32 handIndex) const;
 
         //! Get the Pose action
-        XrAction GetPoseAction() const;
+        XrAction GetPoseAction(AZ::u32 handIndex) const;
 
         //! Get the Vibration action
         XrAction GetVibrationAction() const;
 
         //! Get the Quit action
         XrAction GetQuitAction() const;
-    private:
 
-        //! Create a XrAction
+        //! Get any button state
+        bool GetButtonState(const AzFramework::InputChannelId& channelId) const;
+
+        //! Get the X button state
+        bool GetXButtonState() const;
+
+        //! Get the Y button state
+        bool GetYButtonState() const;
+
+        //! Get the A button state
+        bool GetAButtonState() const;
+
+        //! Get the B button state
+        bool GetBButtonState() const;
+
+        //! Get the joystick state for x-axis
+        float GetXJoyStickState(AZ::u32 handIndex) const;
+
+        //! Get the joystick state for y-axis
+        float GetYJoyStickState(AZ::u32 handIndex) const;
+
+        //! Get the Squeeze action
+        float GetSqueezeState(AZ::u32 handIndex) const;
+
+        //! Get the Squeeze action
+        float GetTriggerState(AZ::u32 handIndex) const;
+
+    private:
+        //! Creates an XrAction
         void CreateAction(XrAction& action, XrActionType actionType,
                           const char* actionName, const char* localizedActionName,
-                          uint32_t countSubactionPathCount, const XrPath* subActionPaths);
+                          uint32_t countSubactionPathCount, const XrPath* subActionPaths) const;
+
+
+        void CreateActionSet(const XrInstance& xrInstance);
+        void CreateAllActions(const XrInstance& xrInstance);
+        XrAction GetAction(const AzFramework::InputChannelId& channelId) const;
 
         //! Destroy native objects
         void ShutdownInternal() override;
 
         XrActionSet m_actionSet{ XR_NULL_HANDLE };
-        XrAction m_grabAction{ XR_NULL_HANDLE };
-        XrAction m_poseAction{ XR_NULL_HANDLE };
-        XrAction m_vibrateAction{ XR_NULL_HANDLE };
-        XrAction m_quitAction{ XR_NULL_HANDLE };
-        AZStd::array<XrPath, 2> m_handSubactionPath;
-        AZStd::array<XrSpace, 2> m_handSpace;
-        AZStd::array<float, 2> m_handScale = { { 1.0f, 1.0f } };
-        AZStd::array<XrBool32, 2> m_handActive;
-
-        AZStd::array<XrSpaceLocation, 2> m_handSpaceLocation;
-        AZStd::array<XrSpaceLocation, SpaceType::Count> m_xrVisualizedSpaceLocations;
+
+        XrAction m_hapticAction{};
+        AZStd::vector<XrActionSuggestedBinding> m_xrActionPaths{};
+        AZStd::unordered_map<AzFramework::InputChannelId, AZStd::size_t> m_xrActionIndices{};
+
+        AZStd::array<XrPath, AZ::RPI::XRMaxNumControllers> m_handSubactionPath{};
+        AZStd::array<XrSpace, AZ::RPI::XRMaxNumControllers> m_handSpace{};
+        AZStd::array<float, AZ::RPI::XRMaxNumControllers> m_handScale{ { 1.0f, 1.0f } };
+        AZStd::array<XrBool32, AZ::RPI::XRMaxNumControllers> m_handActive{};
+
+        AZStd::array<XrSpaceLocation, AZ::RPI::XRMaxNumControllers> m_handSpaceLocation{};
+        AZStd::array<XrSpaceLocation, SpaceType::Count> m_xrVisualizedSpaceLocations{};
+
+        AzFramework::InputDeviceXRController m_xrController{};
+        AzFramework::InputDeviceXRController::Implementation* m_xrControllerImpl{};
+        bool m_wasQuitPressedLastSync{ false };
     };
 }

+ 8 - 8
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkInstance.h

@@ -17,7 +17,7 @@
 
 namespace OpenXRVk
 {
-    //! Vulkan specific XR instance back-end class that will help manage 
+    //! Vulkan specific XR instance back-end class that will help manage
     //! XR specific vulkan native objects
     class Instance final
     : public XR::Instance
@@ -30,15 +30,14 @@ namespace OpenXRVk
 
         //////////////////////////////////////////////////////////////////////////
         // XR::Instance overrides
-        AZ::RHI::ResultCode InitInstanceInternal(AZ::RHI::ValidationMode m_validationMode) override;
         AZ::RHI::ResultCode InitNativeInstance(AZ::RHI::XRInstanceDescriptor* instanceDescriptor) override;
         AZ::u32 GetNumPhysicalDevices() const override;
         AZ::RHI::ResultCode GetXRPhysicalDevice(AZ::RHI::XRPhysicalDeviceDescriptor* physicalDeviceDescriptor, int32_t index) override;
         //////////////////////////////////////////////////////////////////////////
-		
+
         //! Enumerate supported extension names.
         XR::StringList GetInstanceExtensionNames(const char* layerName = nullptr) const;
-		
+
         //! Enumerate supported layer names.
         XR::StringList GetInstanceLayerNames() const;
 
@@ -72,11 +71,12 @@ namespace OpenXRVk
         //! Ge the active VkPhysicalDevice.
         VkPhysicalDevice GetActivePhysicalDevice() const;
 
-    private:
-
-        //! Clean native objects. 
+    protected:
+        // XR::Instance overrides...
+        AZ::RHI::ResultCode InitInstanceInternal() override;
         void ShutdownInternal() override;
 
+    private:
         XrInstance m_xrInstance = XR_NULL_HANDLE;
         VkInstance m_xrVkInstance = VK_NULL_HANDLE;
         XrFormFactor m_formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY;
@@ -85,7 +85,7 @@ namespace OpenXRVk
         XrSystemId m_xrSystemId = XR_NULL_SYSTEM_ID;
         XR::RawStringList m_requiredLayers;
         XR::RawStringList m_requiredExtensions;
-        GladVulkanContext m_context;
+        GladVulkanContext m_context = {};
         AZStd::unique_ptr<AZ::Vulkan::FunctionLoader> m_functionLoader;
         AZStd::vector<VkPhysicalDevice> m_supportedXRDevices;
         AZ::u32 m_physicalDeviceActiveIndex = 0;

+ 1 - 1
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkPhysicalDevice.h

@@ -14,6 +14,6 @@
 namespace OpenXRVk::PhysicalDevice
 {
     //! API to enumerate and return physical devices.
-    static AZStd::vector<VkPhysicalDevice> EnumerateDeviceList(XrSystemId xrSystemId, XrInstance xrInstance, VkInstance vkInstance); 
+    AZStd::vector<VkPhysicalDevice> EnumerateDeviceList(XrSystemId xrSystemId, XrInstance xrInstance, VkInstance vkInstance); 
 }
 

+ 18 - 8
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSession.h

@@ -8,9 +8,9 @@
 
 #pragma once
 
-#include <AzCore/std/smart_ptr/intrusive_ptr.h>
 #include <OpenXRVk_Platform.h>
 #include <OpenXRVk/OpenXRVkSpace.h>
+#include <OpenXRVk/OpenXRVkInput.h>
 #include <XR/XRSession.h>
 
 namespace OpenXRVk
@@ -31,7 +31,7 @@ namespace OpenXRVk
         //! Process session state when it is updated
         void HandleSessionStateChangedEvent(const XrEventDataSessionStateChanged& stateChangedEvent);
 
-        //! Try and poll the next event 
+        //! Try and poll the next event
         const XrEventDataBaseHeader* TryReadNextEvent();
 
         //! Return the native session
@@ -49,24 +49,34 @@ namespace OpenXRVk
         bool IsExitRenderLoopRequested() const override;
         void PollEvents() override;
         void LocateControllerSpace(AZ::u32 handIndex) override;
-        AZ::RPI::PoseData GetControllerPose(AZ::u32 handIndex) const override;
-        AZ::RPI::PoseData GetViewFrontPose() const override;
+        AZ::RHI::ResultCode GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const override;
+        AZ::RHI::ResultCode GetControllerStagePose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const override;
+        AZ::RHI::ResultCode GetViewFrontPose(AZ::RPI::PoseData& outPoseData) const override;
+        AZ::RHI::ResultCode GetViewLocalPose(AZ::RPI::PoseData& outPoseData) const override;
         float GetControllerScale(AZ::u32 handIndex) const override;
+        float GetXButtonState() const override;
+        float GetYButtonState() const override;
+        float GetAButtonState() const override;
+        float GetBButtonState() const override;
+        float GetXJoyStickState(AZ::u32 handIndex) const override;
+        float GetYJoyStickState(AZ::u32 handIndex) const override;
+        float GetSqueezeState(AZ::u32 handIndex) const override;
+        float GetTriggerState(AZ::u32 handIndex) const override;
         //////////////////////////////////////////////////////////////////////////
 
     private:
-
         void ShutdownInternal() override;
         void LogActionSourceName(XrAction action, const AZStd::string_view actionName) const;
-        
+        Input* GetNativeInput() const;
+
         XrSession m_session = XR_NULL_HANDLE;
         XrSessionState m_sessionState = XR_SESSION_STATE_UNKNOWN;
         XrEventDataBuffer m_eventDataBuffer;
         XrInstance m_xrInstance = XR_NULL_HANDLE;
-        XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR };
+        XrGraphicsBindingVulkan2KHR m_graphicsBinding{ XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR };
 
         bool m_sessionRunning = false;
         bool m_exitRenderLoop = false;
-        bool m_requestRestart = false;        
+        bool m_requestRestart = false;
     };
 }

+ 3 - 4
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSpace.h

@@ -34,11 +34,11 @@ namespace OpenXRVk
     public:
         AZ_CLASS_ALLOCATOR(Space, AZ::SystemAllocator, 0);
         AZ_RTTI(Space, "{E99557D0-9061-4691-9524-CE0ACC3A14FA}", XR::Space);
-        
-        static XR::Ptr<Space> Create();           
+
+        static XR::Ptr<Space> Create();
         AZ::RHI::ResultCode InitInternal() override;
         void ShutdownInternal() override;
-    
+
         //!Initialize XrSpace per SpaceType we want to track
         void CreateVisualizedSpaces(XrSession xrSession);
 
@@ -49,7 +49,6 @@ namespace OpenXRVk
         XrSpace GetXrSpace(SpaceType spaceType) const;
 
     private:
-
         //! XrPose specific matrix translation, Rotation functions
         XrPosef Identity();
         XrPosef Translation(const XrVector3f& translation);

+ 4 - 7
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkSwapChain.h

@@ -66,14 +66,11 @@ namespace OpenXRVk
             XrSwapchain m_handle = XR_NULL_HANDLE;
         };
 
-        //! Assign the correct native Swapchain image based on the swapchain index and swapchain image index
+        // XR::SwapChain overrides...
         AZ::RHI::ResultCode GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const override;
-
-        //! Return the recommended swapchain width
         AZ::u32 GetSwapChainWidth(AZ::u32 viewIndex) const override;
-
-        //! Return the recommended swapchain height
         AZ::u32 GetSwapChainHeight(AZ::u32 viewIndex) const override;
+        AZ::RHI::Format GetSwapChainFormat(AZ::u32 viewIndex) const override;
 
         //! Get the view configurations supported by the drivers
         AZStd::vector<XrViewConfigurationView> GetViewConfigs() const;
@@ -87,10 +84,10 @@ namespace OpenXRVk
         void ShutdownInternal() override;
 
         //! Return supported swapchain image format
-        AZ::s64 SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const;
+        VkFormat SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const;
         
         AZStd::vector<XrViewConfigurationView> m_configViews;
-        AZ::s64 m_colorSwapChainFormat{ -1 };
+        VkFormat m_colorSwapChainFormat{ VK_FORMAT_UNDEFINED };
         AZ::u32 m_mipCount = 1;
         AZ::u32 m_faceCount = 1;
         AZ::u32 m_arraySize = 1;

+ 12 - 9
Gems/OpenXRVk/Code/Include/OpenXRVk/OpenXRVkUtils.h

@@ -8,7 +8,6 @@
 
 #pragma once
 
-#include <Atom/RHI.Reflect/Base.h>
 #include <OpenXRVk_Platform.h>
 #include <XR/XRBase.h>
 
@@ -34,33 +33,37 @@ namespace OpenXRVk
 {
 
 #define RETURN_XR_RESULT_IF_UNSUCCESSFUL(result) \
-    if (result != XR_SUCCESS) {\
-        return result;\
+    if ((result) != XR_SUCCESS) {\
+        return (result);\
     }
 
 #define RETURN_IF_UNSUCCESSFUL(result) \
-    if (result != XR_SUCCESS) {\
+    if ((result) != XR_SUCCESS) {\
         return;\
     }
-    
+
 #define WARN_IF_UNSUCCESSFUL(result) \
-    if (result != XR_SUCCESS) {\
+    if ((result) != XR_SUCCESS) {\
         AZ_Warning("OpenXRVk", false, "Warning error code: %s", to_string(result));\
     }
 
 #define ASSERT_IF_UNSUCCESSFUL(result) \
-    if (result != XR_SUCCESS) {\
+    if ((result) != XR_SUCCESS) {\
         AZ_Assert(false, "Assert error code: %s", to_string(result));\
     }
 
+
     AZ::RHI::ResultCode ConvertResult(XrResult xrResult);
     bool IsSuccess(XrResult result);
     bool IsError(XrResult result);
     const char* GetResultString(const XrResult result);
     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. 
-    //! Iterate through the characters while caching the starting pointer to a string 
+    //! 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
     //! and every time ' ' is encountered replace it with '\0' to indicate the end of a string.
     AZStd::vector<const char*> ParseExtensionString(char* names);
+
+    //! Disable certain extensions because function pointers didn't load correctly.
+    void FilterAvailableExtensions(GladVulkanContext& context);
 }

+ 9 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/OpenXRVk_Platform.h

@@ -0,0 +1,9 @@
+/*
+ * 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

+ 1 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/Mac/platform_private_mac_files.cmake

@@ -7,4 +7,5 @@
 #
 
 set(FILES
+    OpenXRVk_Platform.h
 )

+ 9 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/OpenXRVk_Platform.h

@@ -0,0 +1,9 @@
+/*
+ * 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

+ 1 - 0
Gems/OpenXRVk/Code/Include/OpenXRVk/Platform/iOS/platform_private_ios_files.cmake

@@ -7,4 +7,5 @@
 #
 
 set(FILES
+    OpenXRVk_Platform.h
 )

+ 759 - 0
Gems/OpenXRVk/Code/Source/InputDeviceXRController.cpp

@@ -0,0 +1,759 @@
+/*
+ * 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 <OpenXRVk/InputDeviceXRController.h>
+
+#include <AzCore/RTTI/BehaviorContext.h>
+#include <AzFramework/Input/Utils/AdjustAnalogInputForDeadZone.h>
+
+// Debug Draw
+#include <AzCore/Console/IConsole.h>
+#include <Atom/RPI.Public/ViewportContext.h>
+#include <Atom/RPI.Public/ViewportContextBus.h>
+
+namespace OpenXRVk
+{
+    // Cvar to enable/disable debug drawing of xr controller data on screen.
+    // No "on change" function defined here, just read the state of the bool
+    // elsewhere in the draw function.
+    AZ_CVAR(bool, xr_DebugDrawInput, 0,
+        nullptr, AZ::ConsoleFunctorFlags::Null,
+        "Turn off/on debug drawing of XR Input state");
+
+} // namespace OpenXRVk
+
+
+namespace AzFramework
+{
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    bool InputDeviceXRController::IsXRControllerDevice(const InputDeviceId& inputDeviceId)
+    {
+        // Only need to check the name (crc) to check the device is an xr controller type.
+        return (inputDeviceId.GetNameCrc32() == IdForIndex0.GetNameCrc32());
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
+        {
+#define BEHAVIOR_XR_CONSTANT(channel)       Constant(channel.GetName(), BehaviorConstant(channel.GetName()))
+
+            behaviorContext->Class<InputDeviceXRController>()
+                ->Attribute(AZ::Script::Attributes::Storage, AZ::Script::Attributes::StorageType::RuntimeOwn)
+                ->Constant("name", BehaviorConstant(IdForIndex0.GetName()))
+                // Standard digital buttons...
+                ->BEHAVIOR_XR_CONSTANT(Button::A)
+                ->BEHAVIOR_XR_CONSTANT(Button::B)
+                ->BEHAVIOR_XR_CONSTANT(Button::X)
+                ->BEHAVIOR_XR_CONSTANT(Button::Y)
+                ->BEHAVIOR_XR_CONSTANT(Button::Home)
+                ->BEHAVIOR_XR_CONSTANT(Button::Menu)
+                ->BEHAVIOR_XR_CONSTANT(Button::L3)
+                ->BEHAVIOR_XR_CONSTANT(Button::R3)
+                // Touch capacitive...
+                ->BEHAVIOR_XR_CONSTANT(Button::TA)
+                ->BEHAVIOR_XR_CONSTANT(Button::TB)
+                ->BEHAVIOR_XR_CONSTANT(Button::TX)
+                ->BEHAVIOR_XR_CONSTANT(Button::TY)
+                ->BEHAVIOR_XR_CONSTANT(Button::TLStick)
+                ->BEHAVIOR_XR_CONSTANT(Button::TRStick)
+                ->BEHAVIOR_XR_CONSTANT(Button::TLRest)
+                ->BEHAVIOR_XR_CONSTANT(Button::TRRest)
+                ->BEHAVIOR_XR_CONSTANT(Button::TLTrig)
+                ->BEHAVIOR_XR_CONSTANT(Button::TRTrig)
+                // Analog triggers...
+                ->BEHAVIOR_XR_CONSTANT(Trigger::LTrigger)
+                ->BEHAVIOR_XR_CONSTANT(Trigger::RTrigger)
+                ->BEHAVIOR_XR_CONSTANT(Trigger::LGrip)
+                ->BEHAVIOR_XR_CONSTANT(Trigger::RGrip)
+                // Thumbsticks (1D)...
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickAxis1D::LX)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickAxis1D::LY)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickAxis1D::RX)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickAxis1D::RY)
+                // Thumbsticks (2D)...
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickAxis2D::L)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickAxis2D::R)
+                // Thumbstick directions...
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::LU)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::LD)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::LL)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::LR)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::RU)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::RD)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::RL)
+                ->BEHAVIOR_XR_CONSTANT(ThumbStickDirection::RR)
+                // Position (3D)...
+                ->BEHAVIOR_XR_CONSTANT(ControllerPosePosition::LPos)
+                ->BEHAVIOR_XR_CONSTANT(ControllerPosePosition::RPos)
+                // Orientation (quaternion)...
+                ->BEHAVIOR_XR_CONSTANT(ControllerPoseOrientation::LOrient)
+                ->BEHAVIOR_XR_CONSTANT(ControllerPoseOrientation::ROrient)
+            ;
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //! Default constructor
+    //! Using the default constructor will not create an implementation.  It is then up to the user
+    //! to call InputDeviceXRController::SetImplementation and supply either a unique_ptr<Implementation>
+    //! or an ImplementationFactory function.
+    InputDeviceXRController::InputDeviceXRController()
+        : InputDeviceXRController(InputDeviceId(Name, 0), nullptr)
+    {
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceXRController::InputDeviceXRController(const InputDeviceId& inputDeviceId,
+                                                     ImplementationFactory implFactoryFn)
+        : InputDevice(inputDeviceId)
+    {
+        // Create all digital button input channels
+        for (const InputChannelId& channelId : Button::All)
+        {
+            auto channel = aznew InputChannelDigital(channelId, *this);
+            m_allChannelsById[channelId] = channel;
+            m_buttonChannelsById[channelId] = channel;
+        }
+
+        // Create all analog trigger input channels
+        for (const InputChannelId& channelId : Trigger::All)
+        {
+            auto channel = aznew InputChannelAnalog(channelId, *this);
+            m_allChannelsById[channelId] = channel;
+            m_triggerChannelsById[channelId] = channel;
+        }
+
+        // Create all 1D thumb-stick input channels
+        for (const InputChannelId& channelId : ThumbStickAxis1D::All)
+        {
+            auto channel = aznew InputChannelAxis1D(channelId, *this);
+            m_allChannelsById[channelId] = channel;
+            m_thumbStick1DChannelsById[channelId] = channel;
+        }
+
+        // Create all 2D thumb-stick input channels
+        for (const InputChannelId& channelId : ThumbStickAxis2D::All)
+        {
+            auto channel = aznew InputChannelAxis2D(channelId, *this);
+            m_allChannelsById[channelId] = channel;
+            m_thumbStick2DChannelsById[channelId] = channel;
+        }
+
+        // Create all analog thumb-stick direction input channels
+        for (const InputChannelId& channelId : ThumbStickDirection::All)
+        {
+            auto channel = aznew InputChannelAnalog(channelId, *this);
+            m_allChannelsById[channelId] = channel;
+            m_thumbStickDirectionChannelsById[channelId] = channel;
+        }
+
+        // Create all 3D controller position input channels
+        for (const InputChannelId& channelId : ControllerPosePosition::All)
+        {
+            auto channel = aznew InputChannelAxis3D(channelId, *this);
+            m_allChannelsById[channelId] = channel;
+            m_controllerPositionChannelsById[channelId] = channel;
+        }
+
+        // Create all Quat controller orientation input channels
+        for (const InputChannelId& channelId : ControllerPoseOrientation::All)
+        {
+            auto channel = aznew InputChannelQuaternion(channelId, *this);
+            m_allChannelsById[channelId] = channel;
+            m_controllerOrientationChannelsById[channelId] = channel;
+        }
+
+        // Create the custom implementation
+        SetImplementation(AZStd::move(implFactoryFn));
+
+        // Connect to haptic feedback request bus
+        InputHapticFeedbackRequestBus::Handler::BusConnect(GetInputDeviceId());
+
+        // Debug Draw
+        DebugDisplayEventBus::Handler::BusConnect();
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceXRController::~InputDeviceXRController()
+    {
+        // Debug Draw
+        DebugDisplayEventBus::Handler::BusDisconnect();
+
+        // Disconnect from haptic feedback request bus
+        InputHapticFeedbackRequestBus::Handler::BusDisconnect(GetInputDeviceId());
+
+        // Destroy the custom implementation
+        m_impl.reset();
+
+        // Destroy all input channels
+        for (const auto& channelById : m_allChannelsById)
+        {
+            delete channelById.second;
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    const InputDevice::InputChannelByIdMap& InputDeviceXRController::GetInputChannelsById() const
+    {
+        return m_allChannelsById;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    bool InputDeviceXRController::IsSupported() const
+    {
+        return m_impl != nullptr;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    bool InputDeviceXRController::IsConnected() const
+    {
+        return m_impl ? m_impl->IsConnected() : false;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::TickInputDevice()
+    {
+        if (m_impl)
+        {
+            m_impl->TickInputDevice();
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::SetVibration(float leftMotorSpeedNormalized, float rightMotorSpeedNormalized)
+    {
+        if (m_impl)
+        {
+            m_impl->SetVibration(leftMotorSpeedNormalized, rightMotorSpeedNormalized);
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::SetImplementation(AZStd::unique_ptr<Implementation> impl)
+    {
+        m_impl = AZStd::move(impl);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::SetImplementation(const ImplementationFactory& implFactoryFn)
+    {
+        if (implFactoryFn)
+        {
+            m_impl.reset(implFactoryFn(*this));
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceXRController::Implementation* InputDeviceXRController::GetImplementation() const
+    {
+        return m_impl.get();
+    }
+
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //! InputDeviceXRController::Implementation
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceXRController::Implementation::Implementation(InputDeviceXRController& inputDevice)
+        : m_inputDevice(inputDevice)
+    {
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::Implementation::BroadcastInputDeviceConnectedEvent() const
+    {
+        m_inputDevice.BroadcastInputDeviceConnectedEvent();
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::Implementation::BroadcastInputDeviceDisconnectedEvent() const
+    {
+        m_inputDevice.BroadcastInputDeviceDisconnectedEvent();
+    }
+
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //! InputDeviceXRController::Implementation::RawXRControllerState
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceXRController::Implementation::RawXRControllerState::RawXRControllerState(ButtonIdToBitMaskMap digitalButtonMap)
+        : m_buttonIdsToBitMasks(AZStd::move(digitalButtonMap))
+        , m_triggerMaxValue(1.f)
+        , m_gripMaxValue(1.f)
+        , m_thumbStickMaxValue(1.f)
+    {
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::Implementation::RawXRControllerState::Reset()
+    {
+        m_digitalButtonStates = 0;
+        m_leftTriggerState = 0.f;
+        m_rightTriggerState = 0.f;
+        m_leftGripState = 0.f;
+        m_rightGripState = 0.f;
+        m_leftThumbStickXState = 0.f;
+        m_leftThumbStickYState = 0.f;
+        m_rightThumbStickXState = 0.f;
+        m_rightThumbStickYState = 0.f;
+        m_leftPositionState = AZ::Vector3::CreateZero();
+        m_rightPositionState = AZ::Vector3::CreateZero();
+        m_leftOrientationState = AZ::Quaternion::CreateIdentity();
+        m_rightOrientationState = AZ::Quaternion::CreateIdentity();
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    bool InputDeviceXRController::Implementation::RawXRControllerState::GetDigitalButtonState(const InputChannelId& channelId) const
+    {
+        if (auto it = m_buttonIdsToBitMasks.find(channelId); it != m_buttonIdsToBitMasks.end())
+        {
+            return (m_digitalButtonStates & it->second) != 0;
+        }
+        return false;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    float InputDeviceXRController::Implementation::RawXRControllerState::GetLeftTriggerAdjustedForDeadZoneAndNormalized() const
+    {
+        return AdjustForDeadZoneAndNormalizeAnalogInput(m_leftTriggerState, m_triggerDeadZoneValue, m_triggerMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    float InputDeviceXRController::Implementation::RawXRControllerState::GetRightTriggerAdjustedForDeadZoneAndNormalized() const
+    {
+        return AdjustForDeadZoneAndNormalizeAnalogInput(m_rightTriggerState, m_triggerDeadZoneValue, m_triggerMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    float InputDeviceXRController::Implementation::RawXRControllerState::GetLeftGripAdjustedForDeadZoneAndNormalized() const
+    {
+        return AdjustForDeadZoneAndNormalizeAnalogInput(m_leftGripState, m_gripDeadZoneValue, m_gripMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    float InputDeviceXRController::Implementation::RawXRControllerState::GetRightGripAdjustedForDeadZoneAndNormalized() const
+    {
+        return AdjustForDeadZoneAndNormalizeAnalogInput(m_rightGripState, m_gripDeadZoneValue, m_gripMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZ::Vector2 InputDeviceXRController::Implementation::RawXRControllerState::GetLeftThumbStickAdjustedForDeadZoneAndNormalized() const
+    {
+        return AdjustForDeadZoneAndNormalizeThumbStickInput(m_leftThumbStickXState, m_leftThumbStickYState,
+                                                            m_leftThumbStickDeadZoneValue, m_thumbStickMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZ::Vector2 InputDeviceXRController::Implementation::RawXRControllerState::GetRightThumbStickAdjustedForDeadZoneAndNormalized() const
+    {
+        return AdjustForDeadZoneAndNormalizeThumbStickInput(m_rightThumbStickXState, m_rightThumbStickYState,
+                                                            m_rightThumbStickDeadZoneValue, m_thumbStickMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZ::Vector2 InputDeviceXRController::Implementation::RawXRControllerState::GetLeftThumbStickNormalizedValues() const
+    {
+        return AZ::Vector2(m_leftThumbStickXState / m_thumbStickMaxValue,
+                           m_leftThumbStickYState / m_thumbStickMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZ::Vector2 InputDeviceXRController::Implementation::RawXRControllerState::GetRightThumbStickNormalizedValues() const
+    {
+        return AZ::Vector2(m_rightThumbStickXState / m_thumbStickMaxValue,
+                           m_rightThumbStickYState / m_thumbStickMaxValue);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::Implementation::ProcessRawControllerState([[maybe_unused]] const RawXRControllerState& rawControllerState)
+    {
+        // Update digital button channels...
+        for (const auto& [channelId, bitMask] : rawControllerState.m_buttonIdsToBitMasks)
+        {
+            const bool buttonState = (rawControllerState.m_digitalButtonStates & bitMask) != 0;
+            m_inputDevice.m_buttonChannelsById[channelId]->ProcessRawInputEvent(buttonState);
+        }
+
+        using xrc = InputDeviceXRController;
+
+        // Update the analog triggers...
+        const float triggerL = rawControllerState.GetLeftTriggerAdjustedForDeadZoneAndNormalized();
+        const float triggerR = rawControllerState.GetRightTriggerAdjustedForDeadZoneAndNormalized();
+        const float gripL = rawControllerState.GetLeftGripAdjustedForDeadZoneAndNormalized();
+        const float gripR = rawControllerState.GetRightGripAdjustedForDeadZoneAndNormalized();
+        m_inputDevice.m_triggerChannelsById[xrc::Trigger::LTrigger]->ProcessRawInputEvent(triggerL);
+        m_inputDevice.m_triggerChannelsById[xrc::Trigger::RTrigger]->ProcessRawInputEvent(triggerR);
+        m_inputDevice.m_triggerChannelsById[xrc::Trigger::LGrip]->ProcessRawInputEvent(gripL);
+        m_inputDevice.m_triggerChannelsById[xrc::Trigger::RGrip]->ProcessRawInputEvent(gripR);
+
+        // Update thumb-stick channels...
+        const AZ::Vector2 leftThumbStick = rawControllerState.GetLeftThumbStickAdjustedForDeadZoneAndNormalized();
+        const AZ::Vector2 leftThumbStickPreDeadZone = rawControllerState.GetLeftThumbStickNormalizedValues();
+        const float leftStickUp = AZ::GetClamp(leftThumbStick.GetY(), s_thumbStickCenterValue, s_thumbStickMaxValue);
+        const float leftStickDown = fabsf(AZ::GetClamp(leftThumbStick.GetY(), s_thumbStickMinValue, s_thumbStickCenterValue));
+        const float leftStickLeft = fabsf(AZ::GetClamp(leftThumbStick.GetX(), s_thumbStickMinValue, s_thumbStickCenterValue));
+        const float leftStickRight = AZ::GetClamp(leftThumbStick.GetX(), s_thumbStickCenterValue, s_thumbStickMaxValue);
+        const AZ::Vector2 rightThumbStick = rawControllerState.GetRightThumbStickAdjustedForDeadZoneAndNormalized();
+        const AZ::Vector2 rightThumbStickPreDeadZone = rawControllerState.GetRightThumbStickNormalizedValues();
+        const float rightStickUp = AZ::GetClamp(rightThumbStick.GetY(), s_thumbStickCenterValue, s_thumbStickMaxValue);
+        const float rightStickDown = fabsf(AZ::GetClamp(rightThumbStick.GetY(), s_thumbStickMinValue, s_thumbStickCenterValue));
+        const float rightStickLeft = fabsf(AZ::GetClamp(rightThumbStick.GetX(), s_thumbStickMinValue, s_thumbStickCenterValue));
+        const float rightStickRight = AZ::GetClamp(rightThumbStick.GetX(), s_thumbStickCenterValue, s_thumbStickMaxValue);
+
+        m_inputDevice.m_thumbStick2DChannelsById[xrc::ThumbStickAxis2D::L]->ProcessRawInputEvent(leftThumbStick, &leftThumbStickPreDeadZone);
+        m_inputDevice.m_thumbStick1DChannelsById[xrc::ThumbStickAxis1D::LX]->ProcessRawInputEvent(leftThumbStick.GetX());
+        m_inputDevice.m_thumbStick1DChannelsById[xrc::ThumbStickAxis1D::LY]->ProcessRawInputEvent(leftThumbStick.GetY());
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::LU]->ProcessRawInputEvent(leftStickUp);
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::LD]->ProcessRawInputEvent(leftStickDown);
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::LL]->ProcessRawInputEvent(leftStickLeft);
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::LR]->ProcessRawInputEvent(leftStickRight);
+        m_inputDevice.m_thumbStick2DChannelsById[xrc::ThumbStickAxis2D::R]->ProcessRawInputEvent(rightThumbStick, &rightThumbStickPreDeadZone);
+        m_inputDevice.m_thumbStick1DChannelsById[xrc::ThumbStickAxis1D::RX]->ProcessRawInputEvent(rightThumbStick.GetX());
+        m_inputDevice.m_thumbStick1DChannelsById[xrc::ThumbStickAxis1D::RY]->ProcessRawInputEvent(rightThumbStick.GetY());
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::RU]->ProcessRawInputEvent(rightStickUp);
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::RD]->ProcessRawInputEvent(rightStickDown);
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::RL]->ProcessRawInputEvent(rightStickLeft);
+        m_inputDevice.m_thumbStickDirectionChannelsById[xrc::ThumbStickDirection::RR]->ProcessRawInputEvent(rightStickRight);
+
+        // Position update...
+        m_inputDevice.m_controllerPositionChannelsById[xrc::ControllerPosePosition::LPos]
+            ->ProcessRawInputEvent(rawControllerState.m_leftPositionState);
+        m_inputDevice.m_controllerPositionChannelsById[xrc::ControllerPosePosition::RPos]
+            ->ProcessRawInputEvent(rawControllerState.m_rightPositionState);
+
+        // Orientation update...
+        m_inputDevice.m_controllerOrientationChannelsById[xrc::ControllerPoseOrientation::LOrient]
+            ->ProcessRawInputEvent(rawControllerState.m_leftOrientationState);
+        m_inputDevice.m_controllerOrientationChannelsById[xrc::ControllerPoseOrientation::ROrient]
+            ->ProcessRawInputEvent(rawControllerState.m_rightOrientationState);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::Implementation::ResetInputChannelStates()
+    {
+        m_inputDevice.ResetInputChannelStates();
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZ::u32 InputDeviceXRController::Implementation::GetInputDeviceIndex() const
+    {
+        return m_inputDevice.GetInputDeviceId().GetIndex();
+    }
+
+
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Debug Draw Related Functions
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if !defined(AZ_RELEASE_BUILD)
+
+    static AZ::Transform GetCameraTransformFromCurrentView()
+    {
+        if (const auto viewportContextMgr = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
+            viewportContextMgr != nullptr)
+        {
+            if (const AZ::RPI::ViewportContextPtr viewportContext = viewportContextMgr->GetDefaultViewportContext();
+                viewportContext != nullptr)
+            {
+                if (const AZ::RPI::ViewPtr view = viewportContext->GetDefaultView();
+                    view != nullptr)
+                {
+                    return view->GetCameraTransform();
+                }
+            }
+        }
+        return AZ::Transform::CreateIdentity();
+    }
+
+    static void DrawControllerAxes(DebugDisplayRequests& debugDisplay, const AZ::Vector3& position, const AZ::Quaternion& orientation)
+    {
+        static const AZ::Color axisColorX(1.f, 0.f, 0.f, 0.9f);
+        static const AZ::Color axisColorY(0.f, 1.f, 0.f, 0.9f);
+        static const AZ::Color axisColorZ(0.f, 0.f, 1.f, 0.9f);
+
+        const auto cameraTransform = GetCameraTransformFromCurrentView();
+        const AZ::Vector3& cameraPosition = cameraTransform.GetTranslation();
+        const AZ::Vector3 controllerPosition = cameraPosition + position;
+
+        const AZ::Transform controllerTransform = AZ::Transform::CreateFromQuaternionAndTranslation(orientation, controllerPosition);
+        debugDisplay.SetColor(axisColorX);
+        debugDisplay.DrawLine(controllerPosition, controllerPosition + controllerTransform.GetBasisX());
+        debugDisplay.SetColor(axisColorY);
+        debugDisplay.DrawLine(controllerPosition, controllerPosition + controllerTransform.GetBasisY());
+        debugDisplay.SetColor(axisColorZ);
+        debugDisplay.DrawLine(controllerPosition, controllerPosition + controllerTransform.GetBasisZ());
+    }
+
+#endif // !AZ_RELEASE_BUILD
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::CheckDebugDrawCheat() const
+    {
+#if !defined(AZ_RELEASE_BUILD)
+        // This looks for specific controller input and will toggle the debug draw cvar.
+        const auto& rawControllerData = m_impl->GetRawState();
+        using xrc = InputDeviceXRController;
+        static bool cheatWasPressed = false;
+
+        // Menu button + Left Trigger pulled past 0.9 will toggle.
+        // To avoid button bounce, block re-toggle until the menu button is released.
+        const bool menuPressed = rawControllerData.GetDigitalButtonState(xrc::Button::Menu);
+        const float leftTrigger = rawControllerData.GetLeftTriggerAdjustedForDeadZoneAndNormalized();
+        if (menuPressed)
+        {
+            if (!cheatWasPressed && leftTrigger > 0.9f)
+            {
+                cheatWasPressed = true;
+                OpenXRVk::xr_DebugDrawInput = !OpenXRVk::xr_DebugDrawInput;
+            }
+        }
+        else
+        {
+            cheatWasPressed = false;
+        }
+#endif // !AZ_RELEASE_BUILD
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceXRController::DrawGlobalDebugInfo()
+    {
+#if !defined(AZ_RELEASE_BUILD)
+        CheckDebugDrawCheat();
+
+        if (!OpenXRVk::xr_DebugDrawInput)
+        {
+            return;
+        }
+
+        DebugDisplayRequestBus::BusPtr debugDisplayBus;
+        DebugDisplayRequestBus::Bind(debugDisplayBus, g_defaultSceneEntityDebugDisplayId);
+        DebugDisplayRequests* debugDisplay{ DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus) };
+        if (!debugDisplay || !IsSupported())
+        {
+            return;
+        }
+
+        // Save previous draw state
+        const AZ::u32 oldDrawState{ debugDisplay->GetState() };
+
+        // ... draw data to the screen ...
+        const auto& rawControllerData = m_impl->GetRawState();
+        DrawControllerAxes(*debugDisplay, rawControllerData.m_leftPositionState, rawControllerData.m_leftOrientationState);
+        DrawControllerAxes(*debugDisplay, rawControllerData.m_rightPositionState, rawControllerData.m_rightOrientationState);
+
+        float drawX = 20.f;     // current draw X
+        float drawY = 20.f;     // current draw Y
+        constexpr float textSize = 0.8f;
+        constexpr float lineHeight = 15.f;
+
+        const AZ::Color whiteColor{ 1.f, 1.f, 1.f, 1.f };
+        const AZ::Color pressedColor{ 0.f, 1.f, 0.2f, 1.f };
+        const AZ::Color touchedColor{ 0.7f, 0.5f, 0.2f, 1.f };
+        const AZ::Color defaultColor{ 0.2f, 0.2f, 0.2f, 0.8f };
+
+        auto printButtonWithTouchState = [&](const InputChannelId& buttonChannel,
+            const InputChannelId& touchedButtonChannel, const char* buttonText)
+        {
+            AZStd::string text{ buttonText };
+            if (rawControllerData.GetDigitalButtonState(buttonChannel))
+            {
+                text.append(" Pressed");
+                debugDisplay->SetColor(pressedColor);
+            }
+            else if (rawControllerData.GetDigitalButtonState(touchedButtonChannel))
+            {
+                text.append(" Touched");
+                debugDisplay->SetColor(touchedColor);
+            }
+            else
+            {
+                debugDisplay->SetColor(defaultColor);
+            }
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, text.c_str());
+
+            drawY += lineHeight;
+        };
+
+        auto printButtonState = [&](const InputChannelId& buttonChannel, const char* buttonText)
+        {
+            AZStd::string text{ buttonText };
+            if (rawControllerData.GetDigitalButtonState(buttonChannel))
+            {
+                text.append(" Pressed");
+                debugDisplay->SetColor(pressedColor);
+            }
+            else
+            {
+                debugDisplay->SetColor(defaultColor);
+            }
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, text.c_str());
+
+            drawY += lineHeight;
+        };
+
+        auto printButtonTouchOnlyState = [&](const InputChannelId& touchChannel, const char* buttonText)
+        {
+            AZStd::string text{ buttonText };
+            if (rawControllerData.GetDigitalButtonState(touchChannel))
+            {
+                text.append(" Touched");
+                debugDisplay->SetColor(touchedColor);
+            }
+            else
+            {
+                debugDisplay->SetColor(defaultColor);
+            }
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, text.c_str());
+
+            drawY += lineHeight;
+        };
+
+        auto printAnalogWithTouchState = [&](const InputChannelId& touchedChannel, const char* analogText, float value)
+        {
+            AZStd::string text{ analogText };
+            if (!AZ::IsClose(value, 0.f))
+            {
+                text.append(AZStd::string::format(" Pressed: %.2f", value));
+                debugDisplay->SetColor(pressedColor);
+            }
+            else if (rawControllerData.GetDigitalButtonState(touchedChannel))
+            {
+                text.append(" Touched");
+                debugDisplay->SetColor(touchedColor);
+            }
+            else
+            {
+                debugDisplay->SetColor(defaultColor);
+            }
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, text.c_str());
+
+            drawY += lineHeight;
+        };
+
+        auto printAnalogState = [&](const char* analogText, float value)
+        {
+            AZStd::string text{ analogText };
+            if (!AZ::IsClose(value, 0.f))
+            {
+                text.append(AZStd::string::format(" = %.2f", value));
+                debugDisplay->SetColor(pressedColor);
+            }
+            else
+            {
+                debugDisplay->SetColor(defaultColor);
+            }
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, text.c_str());
+
+            drawY += lineHeight;
+        };
+
+        auto print2DThumbStickWithTouchState = [&](const InputChannelId& touchedChannel, const char* thumbStickText, float xvalue, float yvalue)
+        {
+            AZStd::string text{ thumbStickText };
+            if (!AZ::IsClose(xvalue, 0.f) || !AZ::IsClose(yvalue, 0.f))
+            {
+                text.append(AZStd::string::format(" Pressed: (%.2f, %.2f)", xvalue, yvalue));
+                debugDisplay->SetColor(pressedColor);
+            }
+            else if (rawControllerData.GetDigitalButtonState(touchedChannel))
+            {
+                text.append(" Touched");
+                debugDisplay->SetColor(touchedColor);
+            }
+            else
+            {
+                debugDisplay->SetColor(defaultColor);
+            }
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, text.c_str());
+
+            drawY += lineHeight;
+        };
+
+        auto printVector3 = [&](const AZ::Vector3& vec, const char* vectorText)
+        {
+            AZStd::string str{ AZStd::string::format("%s = (%.2f, %.2f, %.2f)", vectorText, vec.GetX(), vec.GetY(), vec.GetZ()) };
+            debugDisplay->SetColor(whiteColor);
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, str.c_str());
+
+            drawY += lineHeight;
+        };
+
+        auto printMatrix3x4 = [&](const AZ::Matrix3x4& matx, const char* matrixText)
+        {
+            debugDisplay->SetColor(whiteColor);
+            AZStd::string str{ AZStd::string::format("%s:", matrixText) };
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, str.c_str());
+            drawY += lineHeight;
+
+            AZ::Vector3 col0, col1, col2, col3;
+            matx.GetColumns(&col0, &col1, &col2, &col3);
+            str = AZStd::string::format("    | %.2f    %.2f    %.2f    %.2f |", col0.GetX(), col1.GetX(), col2.GetX(), col3.GetX());
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, str.c_str());
+            drawY += lineHeight;
+            str = AZStd::string::format("    | %.2f    %.2f    %.2f    %.2f |", col0.GetY(), col1.GetY(), col2.GetY(), col3.GetY());
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, str.c_str());
+            drawY += lineHeight;
+            str = AZStd::string::format("    | %.2f    %.2f    %.2f    %.2f |", col0.GetZ(), col1.GetZ(), col2.GetZ(), col3.GetZ());
+            debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, str.c_str());
+            drawY += lineHeight;
+        };
+
+
+        using xrc = InputDeviceXRController;
+
+        // Left controller...
+        debugDisplay->SetColor(whiteColor);
+        debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, "Left XR Controller");
+        drawY += lineHeight;
+
+        printButtonWithTouchState(xrc::Button::X, xrc::Button::TX, "X");
+        printButtonWithTouchState(xrc::Button::Y, xrc::Button::TY, "Y");
+        printButtonState(xrc::Button::L3, "L3");
+        printButtonState(xrc::Button::Menu, "Menu");
+        printButtonTouchOnlyState(xrc::Button::TLRest, "L ThumbRest");
+        printAnalogWithTouchState(xrc::Button::TLTrig, "L Trigger", rawControllerData.m_leftTriggerState);
+        printAnalogState("L Grip", rawControllerData.m_leftGripState);
+        print2DThumbStickWithTouchState(xrc::Button::TLStick, "L Thumb-Stick",
+            rawControllerData.m_leftThumbStickXState, rawControllerData.m_leftThumbStickYState);
+
+        drawY += (2.f * lineHeight);
+
+        // Right controller...
+        debugDisplay->SetColor(whiteColor);
+        debugDisplay->Draw2dTextLabel(drawX, drawY, textSize, "Right XR Controller");
+        drawY += lineHeight;
+
+        printButtonWithTouchState(xrc::Button::A, xrc::Button::TA, "A");
+        printButtonWithTouchState(xrc::Button::B, xrc::Button::TB, "B");
+        printButtonState(xrc::Button::R3, "R3");
+        printButtonState(xrc::Button::Home, "Home");
+        printButtonTouchOnlyState(xrc::Button::TRRest, "R ThumbRest");
+        printAnalogWithTouchState(xrc::Button::TLTrig, "R Trigger", rawControllerData.m_rightTriggerState);
+        printAnalogState("R Grip", rawControllerData.m_rightGripState);
+        print2DThumbStickWithTouchState(xrc::Button::TRStick, "R Thumb-Stick",
+            rawControllerData.m_rightThumbStickXState, rawControllerData.m_rightThumbStickYState);
+
+        drawY += (2.f * lineHeight);
+
+        // Positions and Orientation
+        printVector3(rawControllerData.m_leftPositionState, "Left Controller Position");
+        printMatrix3x4(AZ::Matrix3x4::CreateFromQuaternion(rawControllerData.m_leftOrientationState), "Left Controller Orientation");
+        printVector3(rawControllerData.m_rightPositionState, "Right Controller Position");
+        printMatrix3x4(AZ::Matrix3x4::CreateFromQuaternion(rawControllerData.m_rightOrientationState), "Right Controller Orientation");
+
+        // Restore previous state
+        debugDisplay->SetState(oldDrawState);
+#endif // !AZ_RELEASE_BUILD
+    }
+
+} // namespace AzFramework

+ 28 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkCommon.h

@@ -0,0 +1,28 @@
+/*
+ * 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
+
+namespace OpenXRVk::Platform
+{
+    //! Initializes the XR loader for this platform.
+    bool OpenXRInitializeLoader();
+
+    //! Called when the device is beginning a frame for processing.
+    //! @note This function is called from the thread related to the presentation queue.
+    void OpenXRBeginFrameInternal();
+
+    //! Called when the device is ending a frame for processing.
+    //! @note This function is called from the thread related to the presentation queue.
+    void OpenXREndFrameInternal();
+
+    //! Called after the EndFrame has been executed.
+    //! @note This function is called from the main thread.
+    void OpenXRPostFrameInternal();
+
+} // namespace OpenXRVk::Platform

+ 53 - 33
Gems/OpenXRVk/Code/Source/OpenXRVkDevice.cpp

@@ -12,6 +12,7 @@
 #include <OpenXRVk/OpenXRVkSwapChain.h>
 #include <OpenXRVk/OpenXRVkSpace.h>
 #include <OpenXRVk/OpenXRVkUtils.h>
+#include <OpenXRVkCommon.h>
 #include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 #include <AzCore/Casting/numeric_cast.h>
 
@@ -53,6 +54,15 @@ namespace OpenXRVk
             extensions.push_back(xrDeviceCreateInfo.vulkanCreateInfo->ppEnabledExtensionNames[i]);
         }
 
+        if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
+        {
+            AZ_Printf("OpenXRVk", "Vulkan device extensions to enable: (%i)\n", extensions.size());
+            for (const AZStd::string& extension : extensions)
+            {
+                AZ_Printf("OpenXRVk", "Name=%s\n", extension.c_str());
+            }
+        }
+
         VkPhysicalDeviceFeatures features{};
         memcpy(&features, xrDeviceCreateInfo.vulkanCreateInfo->pEnabledFeatures, sizeof(features));
 
@@ -80,29 +90,33 @@ namespace OpenXRVk
             return AZ::RHI::ResultCode::Fail;
         }
 
-        // Now that we have created the device, load the function pointers for it.
-        // NOTE: Passing the xr physical device to LoadProcAddresses causes a crash in glad vulkan
-        //       inside 'glad_vk_find_core_vulkan' function when calling 'context->GetPhysicalDeviceProperties'.
-        //       It's OK to pass VK_NULL_HANDLE at the moment, which means glad vulkan will use only device and instance
-        //       to check for vulkan extensions.
-        if (!xrVkInstance->GetFunctionLoader().LoadProcAddresses(
-            &m_context, xrVkInstance->GetNativeInstance(), VK_NULL_HANDLE/*xrVkInstance->GetActivePhysicalDevice()*/, m_xrVkDevice))
         {
-            // Something went wrong loading function pointers, use the glad context from the instance to shut down the device.
-            m_context = xrVkInstance->GetContext();
-            ShutdownInternal();
-            AZ_Error("OpenXRVk", false, "Failed to initialize function loader for the device.");
-            return AZ::RHI::ResultCode::Fail;
+            VkPhysicalDevice xrVkPhysicalDevice = xrVkInstance->GetActivePhysicalDevice();
+            // Now that we have created the device, load the function pointers for it.
+            const bool functionsLoaded = xrVkInstance->GetFunctionLoader().LoadProcAddresses(
+                &m_context, xrVkInstance->GetNativeInstance(), xrVkPhysicalDevice, m_xrVkDevice);
+
+            FilterAvailableExtensions(m_context);
+
+            if (!functionsLoaded)
+            {
+                ShutdownInternal();
+                AZ_Error("OpenXRVk", false, "Failed to initialize function loader for the device.");
+                return AZ::RHI::ResultCode::Fail;
+            }
         }
 
         //Populate the output data of the descriptor
         xrDeviceDescriptor->m_outputData.m_xrVkDevice = m_xrVkDevice;
+        xrDeviceDescriptor->m_outputData.m_context = m_context;
 
         return AZ::RHI::ResultCode::Success;
     }
 
     bool Device::BeginFrameInternal()
     {
+        Platform::OpenXRBeginFrameInternal();
+
         Session* session = static_cast<Session*>(GetSession().get());
         XrSession xrSession = session->GetXrSession();
 
@@ -125,6 +139,8 @@ namespace OpenXRVk
 
     void Device::EndFrameInternal(XR::Ptr<XR::SwapChain> baseSwapChain)
     {
+        Platform::OpenXREndFrameInternal();
+
         Session* session = static_cast<Session*>(GetSession().get());
         Instance* instance = static_cast<Instance*>(GetDescriptor().m_instance.get());
         SwapChain* swapChain = static_cast<SwapChain*>(baseSwapChain.get());
@@ -168,9 +184,13 @@ namespace OpenXRVk
         }
     }
 
+    void Device::PostFrameInternal()
+    {
+        Platform::OpenXRPostFrameInternal();
+    }
+
     bool Device::AcquireSwapChainImageInternal(AZ::u32 viewIndex, XR::SwapChain* baseSwapChain)
     {
-        SwapChain* swapChain = static_cast<SwapChain*>(baseSwapChain);
         XR::SwapChain::View* baseSwapChainView = baseSwapChain->GetView(viewIndex);
         SwapChain::View* swapChainView = static_cast<SwapChain::View*>(baseSwapChainView);
         Space* xrSpace = static_cast<Space*>(GetSession()->GetSpace());
@@ -198,7 +218,7 @@ namespace OpenXRVk
         }
 
         AZ_Assert(m_viewCountOutput == viewCapacityInput, "Size mismatch between xrLocateViews %i and xrEnumerateViewConfigurationViews %i", m_viewCountOutput, viewCapacityInput);
-        AZ_Assert(m_viewCountOutput == swapChain->GetViewConfigs().size(), "Size mismatch between xrLocateViews %i and xrEnumerateViewConfigurationViews %i", m_viewCountOutput, swapChain->GetViewConfigs().size());
+        AZ_Assert(m_viewCountOutput == static_cast<SwapChain*>(baseSwapChain)->GetViewConfigs().size(), "Size mismatch between xrLocateViews %i and xrEnumerateViewConfigurationViews %i", m_viewCountOutput, static_cast<SwapChain*>(baseSwapChain)->GetViewConfigs().size());
 
         m_projectionLayerViews.resize(m_viewCountOutput);
         XrSwapchainImageAcquireInfo acquireInfo{ XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO };
@@ -243,35 +263,35 @@ namespace OpenXRVk
         return m_context;
     }
 
-    AZ::RPI::FovData Device::GetViewFov(AZ::u32 viewIndex) const
+    AZ::RHI::ResultCode Device::GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const
     {
-        AZ::RPI::FovData viewFov;
         if(viewIndex < m_projectionLayerViews.size())
         { 
-            viewFov.m_angleLeft = m_projectionLayerViews[viewIndex].fov.angleLeft;
-            viewFov.m_angleRight = m_projectionLayerViews[viewIndex].fov.angleRight;
-            viewFov.m_angleUp = m_projectionLayerViews[viewIndex].fov.angleUp;
-            viewFov.m_angleDown = m_projectionLayerViews[viewIndex].fov.angleDown;     
+            outFovData.m_angleLeft = m_projectionLayerViews[viewIndex].fov.angleLeft;
+            outFovData.m_angleRight = m_projectionLayerViews[viewIndex].fov.angleRight;
+            outFovData.m_angleUp = m_projectionLayerViews[viewIndex].fov.angleUp;
+            outFovData.m_angleDown = m_projectionLayerViews[viewIndex].fov.angleDown;
+            return AZ::RHI::ResultCode::Success;
         }
-        return viewFov;
+        return AZ::RHI::ResultCode::Fail;
     }
 
-    AZ::RPI::PoseData Device::GetViewPose(AZ::u32 viewIndex) const
-    {
-        AZ::RPI::PoseData viewPose;
+    AZ::RHI::ResultCode Device::GetViewPose(AZ::u32 viewIndex, AZ::RPI::PoseData& outPoseData) const
+    { 
         if (viewIndex < m_projectionLayerViews.size())
         {
             const XrQuaternionf& orientation = m_projectionLayerViews[viewIndex].pose.orientation;
             const XrVector3f& position = m_projectionLayerViews[viewIndex].pose.position;
-            viewPose.orientation = AZ::Quaternion(orientation.x,
-                                                  orientation.y, 
-                                                  orientation.z, 
-                                                  orientation.w);
-            viewPose.position = AZ::Vector3(position.x, 
-                                            position.y, 
-                                            position.z);
-        }        
-        return viewPose;
+            outPoseData.m_orientation.Set(orientation.x,
+                                          orientation.y, 
+                                          orientation.z, 
+                                          orientation.w);
+            outPoseData.m_position.Set(position.x,
+                                       position.y, 
+                                       position.z);
+            return AZ::RHI::ResultCode::Success;
+        }
+        return AZ::RHI::ResultCode::Fail;
     }
 
     XrTime Device::GetPredictedDisplayTime() const

+ 382 - 139
Gems/OpenXRVk/Code/Source/OpenXRVkInput.cpp

@@ -18,111 +18,174 @@ namespace OpenXRVk
 {
     XR::Ptr<Input> Input::Create()
     {
-        return aznew Input;
+        const auto newInput = aznew Input;
+        newInput->m_xrController.SetImplementation(&AzFramework::InputDeviceXRController::Implementation::Create);
+        newInput->m_xrControllerImpl = newInput->m_xrController.GetImplementation();
+        return newInput;
     }
 
     AZ::RHI::ResultCode Input::InitInternal()
     {
-        Instance* xrVkInstance = static_cast<Instance*>(GetDescriptor().m_instance.get());
-        XrInstance xrInstance = xrVkInstance->GetXRInstance();
+        const auto xrVkInstance = static_cast<Instance*>(GetDescriptor().m_instance.get());
+        const XrInstance xrInstance = xrVkInstance->GetXRInstance();
 
         // Create an action set.
-        XrActionSetCreateInfo actionSetInfo{ XR_TYPE_ACTION_SET_CREATE_INFO };
-        azstrcpy(actionSetInfo.actionSetName, sizeof(actionSetInfo.actionSetName), "gameplay");
-        azstrcpy(actionSetInfo.localizedActionSetName, sizeof(actionSetInfo.localizedActionSetName), "Gameplay");
-        actionSetInfo.priority = 0;
-        XrResult result = xrCreateActionSet(xrInstance, &actionSetInfo, &m_actionSet);
-        WARN_IF_UNSUCCESSFUL(result);
+        CreateActionSet(xrInstance);
 
-        // Get the XrPath for the left and right hands - we will use them as subaction paths.
-        result = xrStringToPath(xrInstance, "/user/hand/left", &m_handSubactionPath[static_cast<uint32_t>(XR::Side::Left)]);
-        WARN_IF_UNSUCCESSFUL(result);
-        result = xrStringToPath(xrInstance, "/user/hand/right", &m_handSubactionPath[static_cast<uint32_t>(XR::Side::Right)]);
-        WARN_IF_UNSUCCESSFUL(result);
+        // Create all the XrActions
+        CreateAllActions(xrInstance);
 
-        // Create actions.   
-        // Create an input action for grabbing objects with the left and right hands.
-        CreateAction(m_grabAction, XR_ACTION_TYPE_FLOAT_INPUT, "grab_object", "Grab Object",
-                     aznumeric_cast<uint32_t>(m_handSubactionPath.size()), m_handSubactionPath.data());
-
-        CreateAction(m_poseAction, XR_ACTION_TYPE_POSE_INPUT, "hand_pose", "Hand Pose",
-            aznumeric_cast<uint32_t>(m_handSubactionPath.size()), m_handSubactionPath.data());
-
-        CreateAction(m_vibrateAction, XR_ACTION_TYPE_VIBRATION_OUTPUT, "vibrate_hand", "Vibrate Hand",
-            aznumeric_cast<uint32_t>(m_handSubactionPath.size()), m_handSubactionPath.data());
-
-        CreateAction(m_quitAction, XR_ACTION_TYPE_BOOLEAN_INPUT, "quit_session", "Quit Session", 0, nullptr);
-
-        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> squeezeValuePath;
-        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> posePath;
-        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> hapticPath;
-        AZStd::array<XrPath, static_cast<uint32_t>(XR::Side::Count)> menuClickPath;
-
-        result = xrStringToPath(xrInstance, "/user/hand/left/input/squeeze/value", &squeezeValuePath[static_cast<uint32_t>(XR::Side::Left)]);
-        result = xrStringToPath(xrInstance, "/user/hand/right/input/squeeze/value", &squeezeValuePath[static_cast<uint32_t>(XR::Side::Right)]);
-        result = xrStringToPath(xrInstance, "/user/hand/left/input/grip/pose", &posePath[static_cast<uint32_t>(XR::Side::Left)]);
-        result = xrStringToPath(xrInstance, "/user/hand/right/input/grip/pose", &posePath[static_cast<uint32_t>(XR::Side::Right)]);
-        result = xrStringToPath(xrInstance, "/user/hand/left/output/haptic", &hapticPath[static_cast<uint32_t>(XR::Side::Left)]);
-        result = xrStringToPath(xrInstance, "/user/hand/right/output/haptic", &hapticPath[static_cast<uint32_t>(XR::Side::Right)]);
-        result = xrStringToPath(xrInstance, "/user/hand/left/input/menu/click", &menuClickPath[static_cast<uint32_t>(XR::Side::Left)]);
-        result = xrStringToPath(xrInstance, "/user/hand/right/input/menu/click", &menuClickPath[static_cast<uint32_t>(XR::Side::Right)]);
-        
-        // Bindings for the Occulus Touch.
+        // Bindings for the Oculus Touch.
         XrPath oculusTouchInteractionProfilePath;
-        result = xrStringToPath(xrInstance, "/interaction_profiles/oculus/touch_controller", &oculusTouchInteractionProfilePath);
-        AZStd::vector<XrActionSuggestedBinding> bindings{ { { m_grabAction, squeezeValuePath[static_cast<uint32_t>(XR::Side::Left)] },
-                                                            { m_grabAction, squeezeValuePath[static_cast<uint32_t>(XR::Side::Right)] },
-                                                            { m_poseAction, posePath[static_cast<uint32_t>(XR::Side::Left)] },
-                                                            { m_poseAction, posePath[static_cast<uint32_t>(XR::Side::Right)] },
-                                                            { m_quitAction, menuClickPath[static_cast<uint32_t>(XR::Side::Left)] },
-                                                            { m_vibrateAction, hapticPath[static_cast<uint32_t>(XR::Side::Left)] },
-                                                            { m_vibrateAction, hapticPath[static_cast<uint32_t>(XR::Side::Right)] } } };
-        XrInteractionProfileSuggestedBinding suggestedBindings{ XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING };
+        AZStd::string controllerProfilePath{ m_xrControllerImpl->GetInputDeviceProfilePath() };
+        [[maybe_unused]] XrResult result = xrStringToPath(xrInstance, controllerProfilePath.data(), &oculusTouchInteractionProfilePath);
+        WARN_IF_UNSUCCESSFUL(result);
+
+        XrInteractionProfileSuggestedBinding suggestedBindings{};
+        suggestedBindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
         suggestedBindings.interactionProfile = oculusTouchInteractionProfilePath;
-        suggestedBindings.suggestedBindings = bindings.data();
-        suggestedBindings.countSuggestedBindings = aznumeric_cast<uint32_t>(bindings.size());
+        suggestedBindings.suggestedBindings = m_xrActionPaths.data();
+        suggestedBindings.countSuggestedBindings = aznumeric_cast<AZ::u32>(m_xrActionPaths.size());
         result = xrSuggestInteractionProfileBindings(xrInstance, &suggestedBindings);
         WARN_IF_UNSUCCESSFUL(result);
-        
+
+        //Init the location data so we dont read bad data when the device is in a bad state at start
+        for (int i = 0; i < AZ::RPI::XRMaxNumControllers; i++)
+        {
+            m_handSpaceLocation[i].pose.orientation.x = 0.0f;
+            m_handSpaceLocation[i].pose.orientation.y = 0.0f;
+            m_handSpaceLocation[i].pose.orientation.z = 0.0f;
+            m_handSpaceLocation[i].pose.orientation.w = 0.0f;
+            m_handSpaceLocation[i].pose.position.x = 0.0f;
+            m_handSpaceLocation[i].pose.position.y = 0.0f;
+            m_handSpaceLocation[i].pose.position.z = 0.0f;
+        }
+
+        for (int i = 0; i < SpaceType::Count; i++)
+        {
+            m_xrVisualizedSpaceLocations[i].pose.orientation.x = 0.0f;
+            m_xrVisualizedSpaceLocations[i].pose.orientation.y = 0.0f;
+            m_xrVisualizedSpaceLocations[i].pose.orientation.z = 0.0f;
+            m_xrVisualizedSpaceLocations[i].pose.orientation.w = 0.0f;
+            m_xrVisualizedSpaceLocations[i].pose.position.x = 0.0f;
+            m_xrVisualizedSpaceLocations[i].pose.position.y = 0.0f;
+            m_xrVisualizedSpaceLocations[i].pose.position.z = 0.0f;
+        }
         return ConvertResult(result);
     }
 
     void Input::CreateAction(XrAction& action, XrActionType actionType,
-                                  const char* actionName, const char* localizedActionName,
-                                  uint32_t countSubactionPathCount, const XrPath* subActionPaths)
+                             const char* actionName, const char* localizedActionName,
+                             uint32_t countSubactionPathCount, const XrPath* subActionPaths) const
     {
-        XrActionCreateInfo actionInfo{ XR_TYPE_ACTION_CREATE_INFO };
+        XrActionCreateInfo actionInfo{};
+        actionInfo.type = XR_TYPE_ACTION_CREATE_INFO;
         actionInfo.actionType = actionType;
         azstrcpy(actionInfo.actionName, sizeof(actionInfo.actionName), actionName);
         azstrcpy(actionInfo.localizedActionName, sizeof(actionInfo.localizedActionName), localizedActionName);
         actionInfo.countSubactionPaths = countSubactionPathCount;
         actionInfo.subactionPaths = subActionPaths;
-        [[maybe_unused]] XrResult result = xrCreateAction(m_actionSet, &actionInfo, &action);
+
+        [[maybe_unused]] const XrResult result = xrCreateAction(m_actionSet, &actionInfo, &action);
+        WARN_IF_UNSUCCESSFUL(result);
+    }
+
+    void Input::CreateActionSet(const XrInstance& xrInstance)
+    {
+        // Create an action set.
+        XrActionSetCreateInfo actionSetInfo{};
+        actionSetInfo.type = XR_TYPE_ACTION_SET_CREATE_INFO;
+        azstrcpy(actionSetInfo.actionSetName, sizeof(actionSetInfo.actionSetName), "gameplay");
+        azstrcpy(actionSetInfo.localizedActionSetName, sizeof(actionSetInfo.localizedActionSetName), "Gameplay");
+        actionSetInfo.priority = 0;
+
+        [[maybe_unused]] const XrResult result = xrCreateActionSet(xrInstance, &actionSetInfo, &m_actionSet);
+        WARN_IF_UNSUCCESSFUL(result);
+    }
+
+    void Input::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<uint32_t>(XR::Side::Left)]);
+        WARN_IF_UNSUCCESSFUL(result);
+        result = xrStringToPath(xrInstance, rightHandPath.data(), &m_handSubactionPath[static_cast<uint32_t>(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<AZ::u32>(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 Input::InitializeActionSpace(XrSession xrSession)
     {
-        XrActionSpaceCreateInfo actionSpaceInfo{ XR_TYPE_ACTION_SPACE_CREATE_INFO };
-        actionSpaceInfo.action = m_poseAction;
+        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<uint32_t>(XR::Side::Left)];
+
         XrResult result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast<uint32_t>(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<uint32_t>(XR::Side::Right)];
+
         result = xrCreateActionSpace(xrSession, &actionSpaceInfo, &m_handSpace[static_cast<uint32_t>(XR::Side::Right)]);
         WARN_IF_UNSUCCESSFUL(result);
 
         return ConvertResult(result);
     }
 
-    AZ::RHI::ResultCode Input::InitializeActionSets(XrSession xrSession)
+    AZ::RHI::ResultCode Input::InitializeActionSets(XrSession xrSession) const
     {
-        XrSessionActionSetsAttachInfo attachInfo{ XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO };
+        XrSessionActionSetsAttachInfo attachInfo{};
+        attachInfo.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
         attachInfo.countActionSets = 1;
         attachInfo.actionSets = &m_actionSet;
-        XrResult result = xrAttachSessionActionSets(xrSession, &attachInfo);
+
+        const XrResult result = xrAttachSessionActionSets(xrSession, &attachInfo);
         WARN_IF_UNSUCCESSFUL(result);
 
         return ConvertResult(result);
@@ -130,96 +193,212 @@ namespace OpenXRVk
 
     void Input::ShutdownInternal()
     {
-        if (m_actionSet != XR_NULL_HANDLE) 
+        if (m_actionSet != XR_NULL_HANDLE)
         {
-            for (auto hand : { XR::Side::Left, XR::Side::Right }) 
+            for (const auto hand : { XR::Side::Left, XR::Side::Right })
             {
-                xrDestroySpace(m_handSpace[static_cast<uint32_t>(hand)]);
+                xrDestroySpace(m_handSpace[static_cast<AZ::u32>(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 Input::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 Input::PollActions()
     {
-        Session* session = static_cast<Session*>(GetDescriptor().m_session.get());
+        const auto session = static_cast<Session*>(GetDescriptor().m_session.get());
         XrSession xrSession = session->GetXrSession();
-        Device* device = static_cast<Device*>(GetDescriptor().m_device.get());
+        const auto device = static_cast<Device*>(GetDescriptor().m_device.get());
         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{ XR_TYPE_ACTIONS_SYNC_INFO };
+        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;
+        }
 
-        // Get pose and grab action state and start haptic vibrate when hand is 90% squeezed for testing purposes
-        for (auto hand : { XR::Side::Left, XR::Side::Right })
+        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 getInfo{ XR_TYPE_ACTION_STATE_GET_INFO };
-            getInfo.action = m_grabAction;
-            getInfo.subactionPath = m_handSubactionPath[static_cast<uint32_t>(hand)];
+            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;
 
-            XrActionStateFloat grabValue{ XR_TYPE_ACTION_STATE_FLOAT };
-            result = xrGetActionStateFloat(xrSession, &getInfo, &grabValue);
+            const XrResult result = xrGetActionStateFloat(xrSession, &getAnalogInfo, &analogValue);
             WARN_IF_UNSUCCESSFUL(result);
-            if (grabValue.isActive == XR_TRUE)
+
+            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<AZ::u32>(XR::Side::Left)] = 1.f - 0.5f * rawControllerData.m_leftGripState;
+        m_handScale[static_cast<AZ::u32>(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)
             {
-                // Scale the rendered hand by 1.0f (open) to 0.5f (fully squeezed).
-                m_handScale[static_cast<uint32_t>(hand)] = 1.0f - 0.5f * grabValue.currentState;
-                if (grabValue.currentState > 0.9f)
-                {
-                    XrHapticVibration vibration{ XR_TYPE_HAPTIC_VIBRATION };
-                    vibration.amplitude = 0.5;
-                    vibration.duration = XR_MIN_HAPTIC_DURATION;
-                    vibration.frequency = XR_FREQUENCY_UNSPECIFIED;
-
-                    XrHapticActionInfo hapticActionInfo{ XR_TYPE_HAPTIC_ACTION_INFO };
-                    hapticActionInfo.action = m_vibrateAction;
-                    hapticActionInfo.subactionPath = m_handSubactionPath[static_cast<uint32_t>(hand)];
-                    result = xrApplyHapticFeedback(xrSession, &hapticActionInfo, (XrHapticBaseHeader*)&vibration);
-                    WARN_IF_UNSUCCESSFUL(result);
-                }
+                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<XrHapticBaseHeader*>(&hapticVibration));
+                WARN_IF_UNSUCCESSFUL(result);
             }
+        };
+
+        setHapticVibration(static_cast<AZ::u32>(XR::Side::Left), rawControllerData.m_leftMotorVibrationValue);
+        setHapticVibration(static_cast<AZ::u32>(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;
+
+        // 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<AZ::u32>(hand));
+
+            XrActionStatePose poseState{};
+            poseState.type = XR_TYPE_ACTION_STATE_POSE;
 
-            getInfo.action = m_poseAction;
-            XrActionStatePose poseState{ XR_TYPE_ACTION_STATE_POSE };
             result = xrGetActionStatePose(xrSession, &getInfo, &poseState);
             WARN_IF_UNSUCCESSFUL(result);
-            m_handActive[static_cast<uint32_t>(hand)] = poseState.isActive;
+            m_handActive[static_cast<AZ::u32>(hand)] = poseState.isActive;
 
-            LocateControllerSpace(device->GetPredictedDisplayTime(), session->GetXrSpace(OpenXRVk::SpaceType::View), static_cast<uint32_t>(hand));
-        }  
+            LocateControllerSpace(device->GetPredictedDisplayTime(), session->GetXrSpace(OpenXRVk::SpaceType::View), static_cast<AZ::u32>(hand));
+        }
 
-        //Cache 3d location information
-        for (uint32_t i = 0; i < static_cast<uint32_t>(SpaceType::Count); i++)
+        // Cache 3d location information
+        for (AZ::u32 i = 0; i < static_cast<AZ::u32>(SpaceType::Count); i++)
         {
-            SpaceType spaceType = static_cast<SpaceType>(i);
+            const auto spaceType = static_cast<SpaceType>(i);
             LocateVisualizedSpace(device->GetPredictedDisplayTime(), session->GetXrSpace(spaceType),
-                                    session->GetXrSpace(OpenXRVk::SpaceType::View), spaceType);
+                                  session->GetXrSpace(OpenXRVk::SpaceType::View), spaceType);
         }
 
-        // There were no subaction paths specified for the quit action, because we don't care which hand did it.
-        XrActionStateGetInfo getInfo{ XR_TYPE_ACTION_STATE_GET_INFO, nullptr, m_quitAction, XR_NULL_PATH };
-        XrActionStateBoolean quitValue{ XR_TYPE_ACTION_STATE_BOOLEAN };
-        result = xrGetActionStateBoolean(xrSession, &getInfo, &quitValue);
-        WARN_IF_UNSUCCESSFUL(result);
-        if ((quitValue.isActive == XR_TRUE) && (quitValue.changedSinceLastSync == XR_TRUE) && (quitValue.currentState == XR_TRUE))
+        // XR to AZ vector conversion...
+        // Goes from y-up to z-up configuration (keeping Right Handed system)
+        const auto convertVector3 = [](const XrVector3f& xrVec3) -> AZ::Vector3
+        {
+            return AZ::Vector3{ xrVec3.x, -xrVec3.z, xrVec3.y };
+        };
+
+        // XR to AZ quaternion conversion...
+        // Goes from y-up to z-up configuration (keeping Right Handed system)
+        const auto convertQuat = [](const XrQuaternionf& xrQuat) -> AZ::Quaternion
+        {
+            return AZ::Quaternion{ xrQuat.x, -xrQuat.z, xrQuat.y, xrQuat.w };
+        };
+
+        rawControllerData.m_leftPositionState = convertVector3(m_handSpaceLocation[static_cast<AZ::u32>(XR::Side::Left)].pose.position);
+        rawControllerData.m_rightPositionState = convertVector3(m_handSpaceLocation[static_cast<AZ::u32>(XR::Side::Right)].pose.position);
+
+        rawControllerData.m_leftOrientationState = convertQuat(m_handSpaceLocation[static_cast<AZ::u32>(XR::Side::Left)].pose.orientation);
+        rawControllerData.m_rightOrientationState = convertQuat(m_handSpaceLocation[static_cast<AZ::u32>(XR::Side::Right)].pose.orientation);
+
+        // 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;
     }
 
-    void Input::LocateControllerSpace(XrTime predictedDisplayTime, XrSpace baseSpace, uint32_t handIndex)
+    void Input::LocateControllerSpace(XrTime predictedDisplayTime, XrSpace baseSpace, AZ::u32 handIndex)
     {
-        XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION };
-        XrResult result = xrLocateSpace(m_handSpace[handIndex], baseSpace, predictedDisplayTime, &spaceLocation);
-        if (result== XR_SUCCESS)
+        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) 
+                (spaceLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0)
             {
                 m_handSpaceLocation[handIndex] = spaceLocation;
             }
@@ -228,9 +407,10 @@ namespace OpenXRVk
 
     void Input::LocateVisualizedSpace(XrTime predictedDisplayTime, XrSpace space, XrSpace baseSpace, OpenXRVk::SpaceType visualizedSpaceType)
     {
-        XrSpaceLocation spaceLocation{ XR_TYPE_SPACE_LOCATION };
-        XrResult result = xrLocateSpace(space, baseSpace, predictedDisplayTime, &spaceLocation);
-        if (result == XR_SUCCESS)
+        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)
@@ -240,55 +420,118 @@ namespace OpenXRVk
         }
     }
 
-    AZ::RPI::PoseData Input::GetControllerPose(AZ::u32 viewIndex) const
+    AZ::RHI::ResultCode Input::GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const
     {
-        AZ::RPI::PoseData viewPose;
-        if (viewIndex < m_handSpaceLocation.size())
+        if (handIndex < AZStd::size(m_handSpaceLocation))
         {
-            const XrQuaternionf& orientation = m_handSpaceLocation[viewIndex].pose.orientation;
-            const XrVector3f& position = m_handSpaceLocation[viewIndex].pose.position;
-            viewPose.orientation = AZ::Quaternion(orientation.x, orientation.y, orientation.z, orientation.w);
-            viewPose.position = AZ::Vector3(position.x, position.y, position.z);
+            const XrQuaternionf& orientation = m_handSpaceLocation[handIndex].pose.orientation;
+            const XrVector3f& position = m_handSpaceLocation[handIndex].pose.position;
+            outPoseData.m_orientation.Set(orientation.x, orientation.y, orientation.z, orientation.w);
+            outPoseData.m_position.Set(position.x, position.y, position.z);
+            return AZ::RHI::ResultCode::Success;
         }
-        return viewPose;
+        return AZ::RHI::ResultCode::Fail;
     }
 
-    AZ::RPI::PoseData Input::GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType) const
+    AZ::RHI::ResultCode Input::GetVisualizedSpacePose(OpenXRVk::SpaceType visualizedSpaceType, AZ::RPI::PoseData& outPoseData) const
     {
-        AZ::RPI::PoseData viewPose;
-        uint32_t spaceIndex = static_cast<uint32_t>(visualizedSpaceType);
-        if (spaceIndex < m_xrVisualizedSpaceLocations.size())
+        const auto spaceIndex = static_cast<AZ::u32>(visualizedSpaceType);
+        if (spaceIndex < AZStd::size(m_xrVisualizedSpaceLocations))
         {
             const XrQuaternionf& orientation = m_xrVisualizedSpaceLocations[spaceIndex].pose.orientation;
             const XrVector3f& position = m_xrVisualizedSpaceLocations[spaceIndex].pose.position;
-            viewPose.orientation = AZ::Quaternion(orientation.x, orientation.y, orientation.z, orientation.w);
-            viewPose.position = AZ::Vector3(position.x, position.y, position.z);
+            outPoseData.m_orientation.Set(orientation.x, orientation.y, orientation.z, orientation.w);
+            outPoseData.m_position.Set(position.x, position.y, position.z);
+            return AZ::RHI::ResultCode::Success;
         }
-        return viewPose;
+        return AZ::RHI::ResultCode::Fail;
     }
 
-    float Input::GetControllerScale(AZ::u32 viewIndex) const
+    float Input::GetControllerScale(AZ::u32 handIndex) const
     {
-        return m_handScale[viewIndex];
+        return m_handScale[handIndex];
     }
 
-    XrAction Input::GetGrabAction() const
+    XrAction Input::GetSqueezeAction(AZ::u32 handIndex) const
     {
-        return m_grabAction;
+        return (handIndex == static_cast<AZ::u32>(XR::Side::Left))
+            ? GetAction(AzFramework::InputDeviceXRController::Trigger::LGrip)
+            : GetAction(AzFramework::InputDeviceXRController::Trigger::RGrip);
     }
 
-    XrAction Input::GetPoseAction() const
+    XrAction Input::GetPoseAction(AZ::u32 handIndex) const
     {
-        return m_poseAction;
+        return (handIndex == static_cast<AZ::u32>(XR::Side::Left))
+            ? GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::LPos)
+            : GetAction(AzFramework::InputDeviceXRController::ControllerPosePosition::RPos);
     }
 
     XrAction Input::GetVibrationAction() const
     {
-        return m_vibrateAction;
+        return m_hapticAction;
     }
 
     XrAction Input::GetQuitAction() const
     {
-        return m_quitAction;
+        return GetAction(AzFramework::InputDeviceXRController::Button::Home);
+    }
+
+    bool Input::GetButtonState(const AzFramework::InputChannelId& channelId) const
+    {
+        const auto& state = m_xrControllerImpl->GetRawState();
+        return state.GetDigitalButtonState(channelId);
+    }
+
+    bool Input::GetXButtonState() const
+    {
+        return GetButtonState(AzFramework::InputDeviceXRController::Button::X);
+    }
+
+    bool Input::GetYButtonState() const
+    {
+        return GetButtonState(AzFramework::InputDeviceXRController::Button::Y);
+    }
+
+    bool Input::GetAButtonState() const
+    {
+        return GetButtonState(AzFramework::InputDeviceXRController::Button::A);
+    }
+
+    bool Input::GetBButtonState() const
+    {
+        return GetButtonState(AzFramework::InputDeviceXRController::Button::B);
+    }
+
+    float Input::GetXJoyStickState(AZ::u32 handIndex) const
+    {
+        const auto& state = m_xrControllerImpl->GetRawState();
+        return (handIndex == static_cast<AZ::u32>(XR::Side::Left))
+            ? state.m_leftThumbStickXState
+            : state.m_rightThumbStickXState;
+    }
+
+    float Input::GetYJoyStickState(AZ::u32 handIndex) const
+    {
+        const auto& state = m_xrControllerImpl->GetRawState();
+        return (handIndex == static_cast<AZ::u32>(XR::Side::Left))
+            ? state.m_leftThumbStickYState
+            : state.m_rightThumbStickYState;
     }
-}
+
+    float Input::GetSqueezeState(AZ::u32 handIndex) const
+    {
+        const auto& state = m_xrControllerImpl->GetRawState();
+        return (handIndex == static_cast<AZ::u32>(XR::Side::Left))
+            ? state.m_leftGripState
+            : state.m_rightGripState;
+    }
+
+    float Input::GetTriggerState(AZ::u32 handIndex) const
+    {
+        const auto& state = m_xrControllerImpl->GetRawState();
+        return (handIndex == static_cast<AZ::u32>(XR::Side::Left))
+            ? state.m_leftTriggerState
+            : state.m_rightTriggerState;
+    }
+
+} // namespace OpenXRVk

+ 32 - 7
Gems/OpenXRVk/Code/Source/OpenXRVkInstance.cpp

@@ -10,6 +10,7 @@
 #include <OpenXRVk/OpenXRVkUtils.h>
 #include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 #include <AzCore/Casting/numeric_cast.h>
+#include <OpenXRVkCommon.h>
 
 namespace OpenXRVk
 {
@@ -82,8 +83,14 @@ namespace OpenXRVk
         return layerNames;
     }
 
-    AZ::RHI::ResultCode Instance::InitInstanceInternal(AZ::RHI::ValidationMode validationMode)
+    AZ::RHI::ResultCode Instance::InitInstanceInternal()
     {
+        if (!Platform::OpenXRInitializeLoader())
+        {
+            AZ_Error("OpenXRVk", false, "Could not initialize xr loader.");
+            return AZ::RHI::ResultCode::Fail;
+        }
+
         XR::RawStringList optionalLayers;
         XR::RawStringList optionalExtensions = { XR_KHR_VULKAN_ENABLE_EXTENSION_NAME };
 
@@ -95,7 +102,7 @@ namespace OpenXRVk
         XR::RawStringList supportedExtensions = FilterList(optionalExtensions, instanceExtensions);
         m_requiredExtensions.insert(m_requiredExtensions.end(), supportedExtensions.begin(), supportedExtensions.end());
 
-        if (validationMode == AZ::RHI::ValidationMode::Enabled)
+        if (m_validationMode == AZ::RHI::ValidationMode::Enabled)
         {
             AZ_Printf("OpenXRVk", "Available Extensions: (%i)\n", instanceExtensions.size());
             for (const AZStd::string& extension : instanceExtensions)
@@ -103,6 +110,12 @@ namespace OpenXRVk
                 AZ_Printf("OpenXRVk", "Name=%s\n", extension.c_str());
             }
 
+            AZ_Printf("OpenXRVk", "Extensions to enable: (%i)\n", m_requiredExtensions.size());
+            for (const char* extension : m_requiredExtensions)
+            {
+                AZ_Printf("OpenXRVk", "Name=%s\n", extension);
+            }
+
             AZ_Printf("OpenXRVk", "Available Layers: (%i)\n", instanceLayerNames.size());
             for (const AZStd::string& layerName : instanceLayerNames)
             {
@@ -113,8 +126,8 @@ namespace OpenXRVk
         AZ_Assert(m_xrInstance == XR_NULL_HANDLE, "XR Instance is already initialized");
         XrInstanceCreateInfo createInfo{ XR_TYPE_INSTANCE_CREATE_INFO };
         createInfo.next = nullptr;
-        createInfo.enabledExtensionCount = aznumeric_cast<AZ::u32>(supportedExtensions.size());
-        createInfo.enabledExtensionNames = supportedExtensions.data();
+        createInfo.enabledExtensionCount = aznumeric_cast<AZ::u32>(m_requiredExtensions.size());
+        createInfo.enabledExtensionNames = m_requiredExtensions.data();
         createInfo.enabledApiLayerCount = aznumeric_cast<AZ::u32>(supportedLayers.size());
         createInfo.enabledApiLayerNames = supportedLayers.data();
 
@@ -129,7 +142,7 @@ namespace OpenXRVk
             return AZ::RHI::ResultCode::Fail;
         }
 
-        if (validationMode == AZ::RHI::ValidationMode::Enabled)
+        if (m_validationMode == AZ::RHI::ValidationMode::Enabled)
         {
             XrInstanceProperties instanceProperties{ XR_TYPE_INSTANCE_PROPERTIES };
             result = xrGetInstanceProperties(m_xrInstance, &instanceProperties);
@@ -143,7 +156,7 @@ namespace OpenXRVk
             }
         }
 
-        AZ_Assert(m_xrInstance != XR_NULL_HANDLE, "XR Isntance is Null");
+        AZ_Assert(m_xrInstance != XR_NULL_HANDLE, "XR Instance is Null");
         AZ_Assert(m_xrSystemId == XR_NULL_SYSTEM_ID, "XR System id already initialized");
 
         //TODO::Add support for handheld display
@@ -177,7 +190,7 @@ namespace OpenXRVk
         graphicsRequirements.maxApiVersionSupported = legacyRequirements.maxApiVersionSupported;
         graphicsRequirements.minApiVersionSupported = legacyRequirements.minApiVersionSupported;
 
-        if (validationMode == AZ::RHI::ValidationMode::Enabled)
+        if (m_validationMode == AZ::RHI::ValidationMode::Enabled)
         {
             AZ_Printf("OpenXRVk", "graphicsRequirements.maxApiVersionSupported %d.%d.%d\n",
             XR_VERSION_MAJOR(graphicsRequirements.maxApiVersionSupported),
@@ -311,6 +324,15 @@ namespace OpenXRVk
             extensions.push_back(createInfo.vulkanCreateInfo->ppEnabledExtensionNames[i]);
         }
 
+        if (m_validationMode == AZ::RHI::ValidationMode::Enabled)
+        {
+            AZ_Printf("OpenXRVk", "Vulkan instance extensions to enable: (%i)\n", extensions.size());
+            for (const AZStd::string& extension : extensions)
+            {
+                AZ_Printf("OpenXRVk", "Name=%s\n", extension.c_str());
+            }
+        }
+
         VkInstanceCreateInfo instInfo{ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
         memcpy(&instInfo, createInfo.vulkanCreateInfo, sizeof(instInfo));
         instInfo.enabledExtensionCount = aznumeric_cast<AZ::u32>(extensions.size());
@@ -333,8 +355,11 @@ namespace OpenXRVk
             return AZ::RHI::ResultCode::Fail;
         }
 
+        FilterAvailableExtensions(m_context);
+
         //Populate the instance descriptor with the correct VkInstance
         xrInstanceDescriptor->m_outputData.m_xrVkInstance = m_xrVkInstance;
+        xrInstanceDescriptor->m_outputData.m_context = m_context;
 
         //Get the list of Physical devices
         m_supportedXRDevices = PhysicalDevice::EnumerateDeviceList(m_xrSystemId, m_xrInstance, m_xrVkInstance);

+ 3 - 1
Gems/OpenXRVk/Code/Source/OpenXRVkModule.cpp

@@ -9,6 +9,7 @@
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/Module/Module.h>
 #include <OpenXRVk/OpenXRVkSystemComponent.h>
+#include <XRCameraMovementComponent.h>
 
 namespace OpenXRVk
 {   
@@ -24,7 +25,8 @@ namespace OpenXRVk
             : AZ::Module()
         {
             m_descriptors.insert(m_descriptors.end(), {
-                    SystemComponent::CreateDescriptor(),
+                SystemComponent::CreateDescriptor(),
+                XRCameraMovementComponent::CreateDescriptor(),
             });
         }
 

+ 7 - 13
Gems/OpenXRVk/Code/Source/OpenXRVkPhysicalDevice.cpp

@@ -9,26 +9,20 @@
 #include <OpenXRVk/OpenXRVkPhysicalDevice.h>
 #include <OpenXRVk/OpenXRVkUtils.h>
 
-namespace OpenXRVk
+namespace OpenXRVk::PhysicalDevice
 {
-    AZStd::vector<VkPhysicalDevice> PhysicalDevice::EnumerateDeviceList(XrSystemId xrSystemId, XrInstance xrInstance, VkInstance vkInstance)
+    AZStd::vector<VkPhysicalDevice> EnumerateDeviceList(XrSystemId xrSystemId, XrInstance xrInstance, VkInstance vkInstance)
     {
-        AZStd::vector<VkPhysicalDevice> physicalDevices;
-		
-        XrVulkanGraphicsDeviceGetInfoKHR deviceGetInfo{ XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR };
-        deviceGetInfo.systemId = xrSystemId;
-        deviceGetInfo.vulkanInstance = vkInstance;
-        VkPhysicalDevice vkPhysicalDevice = VK_NULL_HANDLE;
-
         PFN_xrGetVulkanGraphicsDeviceKHR pfnGetVulkanGraphicsDeviceKHR = nullptr;
         XrResult result = xrGetInstanceProcAddr(
-	        xrInstance, "xrGetVulkanGraphicsDeviceKHR", reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsDeviceKHR));
+            xrInstance, "xrGetVulkanGraphicsDeviceKHR", reinterpret_cast<PFN_xrVoidFunction*>(&pfnGetVulkanGraphicsDeviceKHR));
         ASSERT_IF_UNSUCCESSFUL(result);
 
-        //TODO::Look into api that can retreive multiple physicall devices if connected
+        //TODO::Look into api that can retreive multiple physical devices if connected
+        VkPhysicalDevice vkPhysicalDevice = VK_NULL_HANDLE;
         result = pfnGetVulkanGraphicsDeviceKHR(xrInstance, xrSystemId, vkInstance, &vkPhysicalDevice);
         ASSERT_IF_UNSUCCESSFUL(result);
-        physicalDevices.push_back(vkPhysicalDevice);
-        return physicalDevices;
+
+        return {vkPhysicalDevice};
     }
 }

+ 70 - 13
Gems/OpenXRVk/Code/Source/OpenXRVkSession.cpp

@@ -14,6 +14,7 @@
 #include <OpenXRVk/OpenXRVkUtils.h>
 #include <AzCore/Debug/Trace.h>
 #include <AzCore/Casting/numeric_cast.h>
+#include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 #include <XR/XRBase.h>
 
 namespace OpenXRVk
@@ -47,7 +48,7 @@ namespace OpenXRVk
         ASSERT_IF_UNSUCCESSFUL(result);
         
         LogReferenceSpaces();
-        Input* xrVkInput = static_cast<Input*>(GetInput());
+        Input* xrVkInput = GetNativeInput();
         xrVkInput->InitializeActionSpace(m_session);
         xrVkInput->InitializeActionSets(m_session);
 
@@ -197,10 +198,12 @@ namespace OpenXRVk
                 {
                     if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
                     {
-                        Input* xrVkInput = static_cast<Input*>(GetInput());
-                        LogActionSourceName(xrVkInput->GetGrabAction(), "Grab");
+                        Input* xrVkInput = GetNativeInput();
+                        LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast<AZ::u32>(XR::Side::Left)), "Squeeze Left");
+                        LogActionSourceName(xrVkInput->GetSqueezeAction(static_cast<AZ::u32>(XR::Side::Right)), "Squeeze Right");
                         LogActionSourceName(xrVkInput->GetQuitAction(), "Quit");
-                        LogActionSourceName(xrVkInput->GetPoseAction(), "Pose");
+                        LogActionSourceName(xrVkInput->GetPoseAction(static_cast<AZ::u32>(XR::Side::Left)), "Pose Left");
+                        LogActionSourceName(xrVkInput->GetPoseAction(static_cast<AZ::u32>(XR::Side::Right)), "Pose Right");
                         LogActionSourceName(xrVkInput->GetVibrationAction(), "Vibrate");
                     }
                     break;
@@ -265,28 +268,77 @@ namespace OpenXRVk
 
     void Session::LocateControllerSpace(AZ::u32 handIndex)
     {
-        Input* xrInput = static_cast<Input*>(GetInput());
+        Input* xrInput = GetNativeInput();
         Device* device = static_cast<Device*>(GetDescriptor().m_device.get());
         Space* space = static_cast<Space*>(GetSpace());
         xrInput->LocateControllerSpace(device->GetPredictedDisplayTime(), space->GetXrSpace(OpenXRVk::SpaceType::View), handIndex);
     }
 
-    AZ::RPI::PoseData Session::GetControllerPose(AZ::u32 handIndex) const
+    AZ::RHI::ResultCode Session::GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const
     {
-        Input* xrInput = static_cast<Input*>(GetInput());
-        return xrInput->GetControllerPose(handIndex);
+        return GetNativeInput()->GetControllerPose(handIndex, outPoseData);
     }
     
+    AZ::RHI::ResultCode Session::GetControllerStagePose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const
+    {
+        Input* xrInput = GetNativeInput();
+        return handIndex == 0 ? xrInput->GetVisualizedSpacePose(OpenXRVk::SpaceType::StageLeft, outPoseData) :
+            xrInput->GetVisualizedSpacePose(OpenXRVk::SpaceType::StageRight, outPoseData);
+    }
+
+    AZ::RHI::ResultCode Session::GetViewFrontPose(AZ::RPI::PoseData& outPoseData) const
+    {
+        return GetNativeInput()->GetVisualizedSpacePose(OpenXRVk::SpaceType::ViewFront, outPoseData);
+    }
+
+    AZ::RHI::ResultCode Session::GetViewLocalPose(AZ::RPI::PoseData& outPoseData) const
+    {
+        return GetNativeInput()->GetVisualizedSpacePose(OpenXRVk::SpaceType::Local, outPoseData);
+    }
+
     float Session::GetControllerScale(AZ::u32 handIndex) const
     {
-        Input* xrInput = static_cast<Input*>(GetInput());
-        return xrInput->GetControllerScale(handIndex);
+        return GetNativeInput()->GetControllerScale(handIndex);
     }
 
-    AZ::RPI::PoseData Session::GetViewFrontPose() const
+    float Session::GetSqueezeState(AZ::u32 handIndex) const
     {
-        Input* xrInput = static_cast<Input*>(GetInput());
-        return xrInput->GetVisualizedSpacePose(OpenXRVk::SpaceType::ViewFront);
+        return GetNativeInput()->GetSqueezeState(handIndex);
+    }
+
+    float Session::GetTriggerState(AZ::u32 handIndex) const
+    {
+        return GetNativeInput()->GetTriggerState(handIndex);
+    }
+
+    float Session::GetXButtonState() const
+    {
+        return (GetNativeInput()->GetXButtonState() ? 1.f : 0.f);
+    }
+
+    float Session::GetYButtonState() const
+    {
+        return (GetNativeInput()->GetYButtonState() ? 1.f : 0.f);
+    }
+
+    float Session::GetAButtonState() const
+    {
+        return (GetNativeInput()->GetAButtonState() ? 1.f : 0.f);
+    }
+
+    float Session::GetBButtonState() const
+    {
+        return (GetNativeInput()->GetBButtonState() ? 1.f : 0.f);
+    }
+
+    float Session::GetXJoyStickState(AZ::u32 handIndex) const
+    {
+        return GetNativeInput()->GetXJoyStickState(handIndex);
+    }
+    
+    float Session::GetYJoyStickState(AZ::u32 handIndex) const
+    {
+        return GetNativeInput()->GetYJoyStickState(handIndex);
     }
 
     XrSession Session::GetXrSession() const
@@ -327,4 +379,9 @@ namespace OpenXRVk
             xrDestroySession(m_session);
         }
     }
+
+    Input* Session::GetNativeInput() const
+    {
+        return static_cast<Input*>(GetInput());
+    }
 }

+ 5 - 5
Gems/OpenXRVk/Code/Source/OpenXRVkSpace.cpp

@@ -25,7 +25,7 @@ namespace OpenXRVk
     void Space::CreateVisualizedSpaces(XrSession xrSession)
     {
         AZ_Assert(xrSession != XR_NULL_HANDLE, "XR session is null");
-  
+
         for (uint32_t i = 0; i < static_cast<uint32_t>(SpaceType::Count); i++)
         {
             XrReferenceSpaceCreateInfo referenceSpaceCreateInfo = GetXrReferenceSpaceCreateInfo(static_cast<SpaceType>(i));
@@ -50,7 +50,7 @@ namespace OpenXRVk
         {
             case SpaceType::View:
             {
-                //Track the view origin used to generate view transforms for the primary viewer (or centroid of 
+                //Track the view origin used to generate view transforms for the primary viewer (or centroid of
                 //view origins if stereo), with +Y up, +X to the right, and -Z forward.
                 referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
                 break;
@@ -64,7 +64,7 @@ namespace OpenXRVk
             }
             case SpaceType::Local:
             {
-                //Track center Local space which is world-locked origin, gravity-aligned to exclude 
+                //Track center Local space which is world-locked origin, gravity-aligned to exclude
                 //pitch and roll, with +Y up, +X to the right, and -Z forward.
                 referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL;
                 break;
@@ -77,14 +77,14 @@ namespace OpenXRVk
             }
             case SpaceType::StageLeft:
             {
-                //Track Left Stage space which is basically the center stage translated to the left and down by 5m. 
+                //Track Left Stage space which is basically the center stage translated to the left and down by 5m.
                 referenceSpaceCreateInfo.poseInReferenceSpace = RotateCCWAboutYAxis(0.f, { -5.f, 0.f, -5.f });
                 referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
                 break;
             }
             case SpaceType::StageRight:
             {
-                //Track Right Stage space which is basically the center stage translated to the right and down by 5m. 
+                //Track Right Stage space which is basically the center stage translated to the right and down by 5m.
                 referenceSpaceCreateInfo.poseInReferenceSpace = RotateCCWAboutYAxis(0.f, { 5.f, 0.f, -5.f });
                 referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE;
                 break;

+ 38 - 17
Gems/OpenXRVk/Code/Source/OpenXRVkSwapChain.cpp

@@ -6,10 +6,14 @@
  *
  */
 
+#include <Atom/RHI.Reflect/Vulkan/Conversion.h>
 #include <Atom/RHI.Reflect/Vulkan/XRVkDescriptors.h>
 #include <AzCore/Casting/numeric_cast.h>
 #include <AzCore/std/containers/set.h>
 #include <AzCore/std/containers/vector.h>
+#include <AzCore/Settings/SettingsRegistry.h>
+#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
+#include <OpenXRVk/OpenXRVkDevice.h>
 #include <OpenXRVk/OpenXRVkInstance.h>
 #include <OpenXRVk/OpenXRVkSession.h>
 #include <OpenXRVk/OpenXRVkSwapChain.h>
@@ -41,7 +45,7 @@ namespace OpenXRVk
         m_height = height;
         return AZ::RHI::ResultCode::Success;
     }
-    
+
     AZ::u32 SwapChain::View::GetCurrentImageIndex() const
     {
         return m_activeImageIndex;
@@ -62,7 +66,7 @@ namespace OpenXRVk
     {
         return m_handle;
     }
-        
+
     AZ::u32 SwapChain::View::GetWidth() const
     {
         return m_width;
@@ -109,13 +113,13 @@ namespace OpenXRVk
         //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);
+        result = xrEnumerateViewConfigurationViews(xrInstance, xrSystemId,
+                                                   viewConfigType, 0, &m_numViews, nullptr);
         WARN_IF_UNSUCCESSFUL(result);
 
         m_configViews.resize(m_numViews, { XR_TYPE_VIEW_CONFIGURATION_VIEW });
-        result = xrEnumerateViewConfigurationViews(xrInstance, xrSystemId, 
-                                                viewConfigType, m_numViews, &m_numViews, m_configViews.data());
+        result = xrEnumerateViewConfigurationViews(xrInstance, xrSystemId,
+                                                   viewConfigType, m_numViews, &m_numViews, m_configViews.data());
         WARN_IF_UNSUCCESSFUL(result);
 
         // Create and cache view buffer for xrLocateViews later.
@@ -129,7 +133,7 @@ namespace OpenXRVk
             result = xrEnumerateSwapchainFormats(xrSession, 0, &swapchainFormatCount, nullptr);
             AZStd::vector<int64_t> swapChainFormats(swapchainFormatCount);
             result = xrEnumerateSwapchainFormats(xrSession, aznumeric_cast<uint32_t>(swapChainFormats.size()),
-                                                &swapchainFormatCount, swapChainFormats.data());
+                                                 &swapchainFormatCount, swapChainFormats.data());
             WARN_IF_UNSUCCESSFUL(result);
             AZ_Assert(swapchainFormatCount == swapChainFormats.size(), "Size mismatch swapchainFormatCount %i swapChainFormats size %i", swapchainFormatCount, swapChainFormats.size());
 
@@ -141,7 +145,7 @@ namespace OpenXRVk
                 AZStd::string swapchainFormatsString;
                 for (int64_t format : swapChainFormats)
                 {
-                    const bool selected = format == m_colorSwapChainFormat;
+                    const bool selected = format == static_cast<int64_t>(m_colorSwapChainFormat);
                     swapchainFormatsString += " ";
                     if (selected)
                     {
@@ -156,10 +160,21 @@ namespace OpenXRVk
                 AZ_Printf("OpenXRVk", "Swapchain Formats: %s\n", swapchainFormatsString.c_str());
             }
 
+            double xrViewResolutionScale = 1.0;
+            if (auto* settingsRegistry = AZ::SettingsRegistry::Get();
+                settingsRegistry != nullptr)
+            {
+                AZ::SettingsRegistryMergeUtils::PlatformGet(*settingsRegistry, xrViewResolutionScale,
+                    "/O3DE/Atom/OpenXR", "ViewResolutionScale");
+            }
+
             // Create a swapchain for each view.
             for (uint32_t i = 0; i < m_numViews; i++)
             {
-                const XrViewConfigurationView& configView = m_configViews[i];
+                XrViewConfigurationView& configView = m_configViews[i];
+
+                configView.recommendedImageRectWidth = static_cast<uint32_t>(static_cast<double>(configView.recommendedImageRectWidth) * xrViewResolutionScale);
+                configView.recommendedImageRectHeight = static_cast<uint32_t>(static_cast<double>(configView.recommendedImageRectHeight) * xrViewResolutionScale);
 
                 if (GetDescriptor().m_validationMode == AZ::RHI::ValidationMode::Enabled)
                 {
@@ -175,7 +190,7 @@ namespace OpenXRVk
                     // Create the xr swapchain.
                     XrSwapchainCreateInfo swapchainCreateInfo{ XR_TYPE_SWAPCHAIN_CREATE_INFO };
                     swapchainCreateInfo.arraySize = m_arraySize;
-                    swapchainCreateInfo.format = m_colorSwapChainFormat;
+                    swapchainCreateInfo.format = static_cast<int64_t>(m_colorSwapChainFormat);
                     swapchainCreateInfo.width = configView.recommendedImageRectWidth;
                     swapchainCreateInfo.height = configView.recommendedImageRectHeight;
                     swapchainCreateInfo.mipCount = m_mipCount;
@@ -189,14 +204,14 @@ namespace OpenXRVk
 
                     AZ::RHI::ResultCode resultCode = viewSwapChain->Init(handle, swapchainCreateInfo.width, swapchainCreateInfo.height);
                     if(resultCode == AZ::RHI::ResultCode::Success)
-                    { 
+                    {
                         m_viewSwapchains.push_back(viewSwapChain);
                     }
                 }
 
                 result = xrEnumerateSwapchainImages(viewSwapChain->GetSwapChainHandle(), 0, &viewSwapChain->m_numImages, nullptr);
                 WARN_IF_UNSUCCESSFUL(result);
-                
+
                 viewSwapChain->m_swapChainImageHeaders.resize(viewSwapChain->m_numImages);
                 viewSwapChain->m_swapchainImages.resize(viewSwapChain->m_numImages);
                 for (AZ::u32 j = 0; j < viewSwapChain->m_numImages; ++j)
@@ -223,20 +238,21 @@ namespace OpenXRVk
         return AZ::RHI::ResultCode::Success;
     }
 
-    AZ::s64 SwapChain::SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const
+    VkFormat SwapChain::SelectColorSwapChainFormat(const AZStd::vector<int64_t>& runtimeFormats) const
     {
         // List of supported color swapchain formats.
-        constexpr AZ::s64 SupportedColorSwapchainFormats[] = { VK_FORMAT_B8G8R8A8_UNORM };
+        constexpr int64_t SupportedColorSwapchainFormats[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM };
 
         auto swapchainFormatIt =
             AZStd::find_first_of(runtimeFormats.begin(), runtimeFormats.end(), AZStd::begin(SupportedColorSwapchainFormats),
                 AZStd::end(SupportedColorSwapchainFormats));
-        if (swapchainFormatIt == runtimeFormats.end()) 
+        if (swapchainFormatIt == runtimeFormats.end())
         {
             AZ_Error("OpenXRVk", false, "No runtime swapchain format supported for color swapchain");
+            return VK_FORMAT_UNDEFINED;
         }
 
-        return *swapchainFormatIt;
+        return static_cast<VkFormat>(*swapchainFormatIt);
     }
 
     AZStd::vector<XrViewConfigurationView> SwapChain::GetViewConfigs() const
@@ -249,7 +265,7 @@ namespace OpenXRVk
         AZ::Vulkan::XRSwapChainDescriptor* xrSwapChainDescriptor = static_cast<AZ::Vulkan::XRSwapChainDescriptor*>(swapchainDescriptor);
         uint32_t swapChainIndex = xrSwapChainDescriptor->m_inputData.m_swapChainIndex;
         uint32_t swapChainImageIndex = xrSwapChainDescriptor->m_inputData.m_swapChainImageIndex;
-        
+
         XR::SwapChain::View* viewSwapChain = GetView(swapChainIndex);
         SwapChain::Image* swapchainImage = static_cast<SwapChain::Image*>(viewSwapChain->m_images[swapChainImageIndex].get());
         xrSwapChainDescriptor->m_outputData.m_nativeImage = swapchainImage->GetNativeImage();
@@ -266,6 +282,11 @@ namespace OpenXRVk
         return m_configViews[viewIndex].recommendedImageRectHeight;
     }
 
+    AZ::RHI::Format SwapChain::GetSwapChainFormat([[maybe_unused]] AZ::u32 viewIndex) const
+    {
+        return AZ::Vulkan::ConvertFormat(m_colorSwapChainFormat);
+    }
+
     void SwapChain::ShutdownInternal()
     {
         for(XR::Ptr<XR::SwapChain::View> viewSwapChain : m_viewSwapchains)

+ 5 - 1
Gems/OpenXRVk/Code/Source/OpenXRVkSystemComponent.cpp

@@ -6,7 +6,9 @@
  *
  */
 
+
 #include <AzCore/Serialization/SerializeContext.h>
+
 #include <OpenXRVk/OpenXRVkDevice.h>
 #include <OpenXRVk/OpenXRVkInput.h>
 #include <OpenXRVk/OpenXRVkInstance.h>
@@ -24,11 +26,13 @@ namespace OpenXRVk
 
     void SystemComponent::Reflect(AZ::ReflectContext* context)
     {
-        if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
         {
             serializeContext->Class<SystemComponent, AZ::Component>()
                 ->Version(1);
         }
+
+        AzFramework::InputDeviceXRController::Reflect(context);
     }
 
     SystemComponent::SystemComponent()

+ 10 - 0
Gems/OpenXRVk/Code/Source/OpenXRVkUtils.cpp

@@ -74,4 +74,14 @@ namespace OpenXRVk
         }
         return list;
     }
+
+    void FilterAvailableExtensions(GladVulkanContext& context)
+    {
+        // In some cases (like when running with the GPU profiler on Quest2) the extension is reported as available
+        // but the function pointers do not load. Disable the extension if that's the case.
+        if (context.EXT_debug_utils && !context.CmdBeginDebugUtilsLabelEXT)
+        {
+            context.EXT_debug_utils = 0;
+        }
+    }
 }

+ 74 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVkCommon_Android.cpp

@@ -0,0 +1,74 @@
+/*
+ * 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 <OpenXRVkCommon.h>
+#include <AzCore/Android/AndroidEnv.h>
+#include <OpenXRVk/OpenXRVkUtils.h>
+
+
+namespace OpenXRVk::Platform
+{
+    bool OpenXRInitializeLoader()
+    {
+        PFN_xrInitializeLoaderKHR initializeLoader = nullptr;
+        XrResult result = xrGetInstanceProcAddr(
+            XR_NULL_HANDLE, "xrInitializeLoaderKHR",
+            reinterpret_cast<PFN_xrVoidFunction*>(&initializeLoader));
+        if (IsError(result))
+        {
+            return false;
+        }
+
+        AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
+        AZ_Assert(androidEnv != nullptr, "Invalid android environment");
+
+        JavaVM* javaVM = nullptr;
+        androidEnv->GetJniEnv()->GetJavaVM(&javaVM);
+        AZ_Assert(javaVM != nullptr, "Invalid Java VM");
+        jobject javaActivity = androidEnv->GetActivityRef();
+
+        XrLoaderInitInfoAndroidKHR loaderInitInfoAndroid;
+        memset(&loaderInitInfoAndroid, 0, sizeof(loaderInitInfoAndroid));
+        loaderInitInfoAndroid.type = XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR;
+        loaderInitInfoAndroid.next = nullptr;
+        loaderInitInfoAndroid.applicationVM = javaVM;
+        loaderInitInfoAndroid.applicationContext = javaActivity;
+
+        result = initializeLoader(reinterpret_cast<const XrLoaderInitInfoBaseHeaderKHR*>(&loaderInitInfoAndroid));
+        if (IsError(result))
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    void OpenXRBeginFrameInternal()
+    {
+    }
+
+    void OpenXREndFrameInternal()
+    {
+        // OpenXR's xrEndFrame function internally uses the application's Java VM (passed in OpenXRInitializeLoader).
+        // xrEndFrame function is called from the thread related to the presentation queue (not the main thread) and
+        // that causes to create a temporary JNI Environment for this thread, which is not optimal.
+        // Calling GetJniEnv() will attach the JNI Environment to this thread.
+        AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
+        AZ_Assert(androidEnv != nullptr, "Invalid android environment");
+        androidEnv->GetJniEnv();
+    }
+
+    void OpenXRPostFrameInternal()
+    {
+        // Now that EndFrame has finished, calling GetJniEnv() again from the main thread
+        // to attach the JNI Environment back to the main thread.
+        AZ::Android::AndroidEnv* androidEnv = AZ::Android::AndroidEnv::Get();
+        AZ_Assert(androidEnv != nullptr, "Invalid android environment");
+        androidEnv->GetJniEnv();
+    }
+}

+ 2 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/OpenXRVk_Traits_Android.h

@@ -6,3 +6,5 @@
  *
  */
 #pragma once
+
+#define O3DE_TRAIT_DISABLE_FAILED_OPENXRVK_TESTS

+ 4 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/platform_private_android_files.cmake

@@ -7,6 +7,10 @@
 #
 
 set(FILES
+    OpenXRVkCommon_Android.cpp
     OpenXRVk_Traits_Android.h
     OpenXRVk_Traits_Platform.h
+    ../Common/Default/InputDeviceXRController_Default.cpp
+    ../Common/Default/OculusTouch_Default.cpp
+    ../Common/Default/OculusTouch_Default.h
 )

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/Android/platform_test_android_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
+    ../Common/Unimplemented/OpenXRVkTest_Unimplemented.cpp
+)

+ 23 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Default/InputDeviceXRController_Default.cpp

@@ -0,0 +1,23 @@
+/*
+ * 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 <OpenXRVk/InputDeviceXRController.h>
+#include "OculusTouch_Default.h"
+
+namespace AzFramework
+{
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceXRController::Implementation* InputDeviceXRController::Implementation::Create(
+        InputDeviceXRController& inputDevice)
+    {
+        // Future versions of this function may be able to select from a variety of different device
+        // types and do so based on knowledge of the hardware, but for now force the Oculus Touch controller.
+        return aznew OpenXRVk::InputDeviceOculusTouch(inputDevice);
+    }
+
+} // namespace AzFramework

+ 161 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.cpp

@@ -0,0 +1,161 @@
+/*
+ * 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 "OculusTouch_Default.h"
+
+#include <OpenXRVk_Platform.h>
+
+namespace OpenXRVk
+{
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //! Map of digital button ids keyed by their button bitmask
+    const AzFramework::InputDeviceXRController::Implementation::ButtonIdToBitMaskMap GetButtonIdToBitMaskMap()
+    {
+        using xrc = AzFramework::InputDeviceXRController;
+        return {
+            { xrc::Button::A, (1 << 0) },
+            { xrc::Button::B, (1 << 1) },
+            { xrc::Button::X, (1 << 2) },
+            { xrc::Button::Y, (1 << 3) },
+            { xrc::Button::Home, (1 << 4) },
+            { xrc::Button::Menu, (1 << 5) },
+            { xrc::Button::L3, (1 << 6) },
+            { xrc::Button::R3, (1 << 7) },
+            { xrc::Button::TA, (1 << 8) },
+            { xrc::Button::TB, (1 << 9) },
+            { xrc::Button::TX, (1 << 10) },
+            { xrc::Button::TY, (1 << 11) },
+            { xrc::Button::TLStick, (1 << 12) },
+            { xrc::Button::TRStick, (1 << 13) },
+            { xrc::Button::TLRest, (1 << 14) },
+            { xrc::Button::TRRest, (1 << 15) },
+            { xrc::Button::TLTrig, (1 << 16) },
+            { xrc::Button::TRTrig, (1 << 17) },
+        };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceOculusTouch::InputDeviceOculusTouch(AzFramework::InputDeviceXRController& inputDevice)
+        : AzFramework::InputDeviceXRController::Implementation(inputDevice)
+        , m_rawControllerState(GetButtonIdToBitMaskMap())
+    {
+        // These are guesses for now
+        m_rawControllerState.m_triggerMaxValue = 1.f;
+        m_rawControllerState.m_triggerDeadZoneValue = 0.1f;
+        m_rawControllerState.m_thumbStickMaxValue = 1.f;
+        m_rawControllerState.m_leftThumbStickDeadZoneValue = 0.1f;
+        m_rawControllerState.m_rightThumbStickDeadZoneValue = 0.1f;
+
+        using xrc = AzFramework::InputDeviceXRController;
+        m_xrPathMap = {
+            { xrc::Button::A, "/user/hand/right/input/a/click" },
+            { xrc::Button::B, "/user/hand/right/input/b/click" },
+            { xrc::Button::X, "/user/hand/left/input/x/click" },
+            { xrc::Button::Y, "/user/hand/left/input/y/click" },
+            { xrc::Button::Home, "/user/hand/right/input/system/click" },
+            { xrc::Button::Menu, "/user/hand/left/input/menu/click" },
+            { xrc::Button::L3, "/user/hand/left/input/thumbstick/click" },
+            { xrc::Button::R3, "/user/hand/right/input/thumbstick/click" },
+            { xrc::Button::TA, "/user/hand/right/input/a/touch" },
+            { xrc::Button::TB, "/user/hand/right/input/b/touch" },
+            { xrc::Button::TX, "/user/hand/left/input/x/touch" },
+            { xrc::Button::TY, "/user/hand/left/input/y/touch" },
+            { xrc::Button::TLStick, "/user/hand/left/input/thumbstick/touch" },
+            { xrc::Button::TRStick, "/user/hand/right/input/thumbstick/touch" },
+            { xrc::Button::TLRest, "/user/hand/left/input/thumbrest/touch" },
+            { xrc::Button::TRRest, "/user/hand/right/input/thumbrest/touch" },
+            { xrc::Button::TLTrig, "/user/hand/left/input/trigger/touch" },
+            { xrc::Button::TRTrig, "/user/hand/right/input/trigger/touch" },
+            { xrc::Trigger::LTrigger, "/user/hand/left/input/trigger/value" },
+            { xrc::Trigger::RTrigger, "/user/hand/right/input/trigger/value" },
+            { xrc::Trigger::LGrip, "/user/hand/left/input/squeeze/value" },
+            { xrc::Trigger::RGrip, "/user/hand/right/input/squeeze/value" },
+            { xrc::ThumbStickAxis1D::LX, "/user/hand/left/input/thumbstick/x" },
+            { xrc::ThumbStickAxis1D::LY, "/user/hand/left/input/thumbstick/y" },
+            { xrc::ThumbStickAxis1D::RX, "/user/hand/right/input/thumbstick/x" },
+            { xrc::ThumbStickAxis1D::RY, "/user/hand/right/input/thumbstick/y" },
+            { xrc::ControllerPosePosition::LPos, "/user/hand/left/input/grip/pose" },
+            { xrc::ControllerPosePosition::RPos, "/user/hand/right/input/grip/pose" },
+            { xrc::ControllerPoseOrientation::LOrient, "/user/hand/left/input/aim/pose" },
+            { xrc::ControllerPoseOrientation::ROrient, "/user/hand/right/input/aim/pose" },
+        };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceOculusTouch::~InputDeviceOculusTouch() = default;
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZStd::string InputDeviceOculusTouch::GetInputChannelPath(const AzFramework::InputChannelId& channelId) const
+    {
+        if (const auto it = m_xrPathMap.find(channelId);
+            it != m_xrPathMap.end())
+        {
+            return{ it->second };
+        }
+        return {};
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZStd::string InputDeviceOculusTouch::GetInputDeviceProfilePath() const
+    {
+        return { "/interaction_profiles/oculus/touch_controller" };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZStd::string InputDeviceOculusTouch::GetLeftHandSubPath() const
+    {
+        return { "/user/hand/left" };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    AZStd::string InputDeviceOculusTouch::GetRightHandSubPath() const
+    {
+        return { "/user/hand/right" };
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceOculusTouch::RegisterTickCallback(TickCallbackFn callbackFn)
+    {
+        m_tickCallback = callbackFn;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceOculusTouch::RawXRControllerState& InputDeviceOculusTouch::GetRawState()
+    {
+        return m_rawControllerState;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    bool InputDeviceOculusTouch::IsConnected() const
+    {
+        return m_isConnected;
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceOculusTouch::SetVibration(float leftMotorSpeedNormalized, float rightMotorSpeedNormalized)
+    {
+        if (m_isConnected)
+        {
+            // Set vibration values on the raw data structure, they will be consumed on the next tick
+            m_rawControllerState.m_leftMotorVibrationValue = leftMotorSpeedNormalized;
+            m_rawControllerState.m_rightMotorVibrationValue = rightMotorSpeedNormalized;
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    void InputDeviceOculusTouch::TickInputDevice()
+    {
+        if (m_tickCallback)
+        {
+            m_tickCallback();
+
+            ProcessRawControllerState(m_rawControllerState);
+        }
+    }
+
+} // namespace OpenXRVk

+ 62 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Default/OculusTouch_Default.h

@@ -0,0 +1,62 @@
+/*
+ * 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 <OpenXRVk/InputDeviceXRController.h>
+#include <AzCore/std/containers/unordered_map.h>
+
+
+namespace OpenXRVk
+{
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    //! Platform/API-specific implementation for Oculus Touch Controller input device
+    class InputDeviceOculusTouch
+        : public AzFramework::InputDeviceXRController::Implementation
+    {
+    public:
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Allocator
+        AZ_CLASS_ALLOCATOR(InputDeviceOculusTouch, AZ::SystemAllocator, 0);
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        explicit InputDeviceOculusTouch(AzFramework::InputDeviceXRController& inputDevice);
+        AZ_DISABLE_COPY_MOVE(InputDeviceOculusTouch);
+        ~InputDeviceOculusTouch() override;
+
+        AZStd::string GetInputChannelPath(const AzFramework::InputChannelId& channelId) const override;
+        AZStd::string GetInputDeviceProfilePath() const override;
+        AZStd::string GetLeftHandSubPath() const override;
+        AZStd::string GetRightHandSubPath() const override;
+        void RegisterTickCallback(TickCallbackFn callbackFn) override;
+        RawXRControllerState& GetRawState() override;
+
+    private:
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! @see AzFramework::InputDeviceXRController::Implementation::IsConnected
+        bool IsConnected() const override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! @see AzFramework::InputDeviceXRController::Implementation::SetVibration
+        void SetVibration(float leftMotorSpeedNormalized, float rightMotorSpeedNormalized) override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        //! @see AzFramework::InputDeviceXRController::Implementation::TickInputDevice
+        void TickInputDevice() override;
+
+        ////////////////////////////////////////////////////////////////////////////////////////////
+        // Data
+        RawXRControllerState m_rawControllerState;  //!< The latest raw xr controller input state
+        bool m_isConnected{}; //!< Is the controller(s) currently connected?
+
+        TickCallbackFn m_tickCallback{ nullptr };
+
+        AZStd::unordered_map<AzFramework::InputChannelId, const AZStd::string_view> m_xrPathMap{};
+    };
+
+} // namespace OpenXRVk

+ 20 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/InputDeviceXRController_Unimplemented.cpp

@@ -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
+ *
+ */
+
+#include <OpenXRVk/InputDeviceXRController.h>
+
+namespace AzFramework
+{
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    InputDeviceXRController::Implementation* InputDeviceXRController::Implementation::Create(
+        InputDeviceXRController& inputDevice)
+    {
+        return nullptr;
+    }
+
+} // namespace AzFramework

+ 29 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/OpenXRVkCommon_Unimplemented.cpp

@@ -0,0 +1,29 @@
+/*
+ * 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 <OpenXRVkCommon.h>
+
+namespace OpenXRVk::Platform
+{
+    bool OpenXRInitializeLoader()
+    {
+        return true;
+    }
+
+    void OpenXRBeginFrameInternal()
+    {
+    }
+
+    void OpenXREndFrameInternal()
+    {
+    }
+
+    void OpenXRPostFrameInternal()
+    {
+    }
+}

+ 24 - 0
Gems/OpenXRVk/Code/Source/Platform/Common/Unimplemented/OpenXRVkTest_Unimplemented.cpp

@@ -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
+ *
+ */
+
+#include <AzTest/AzTest.h>
+
+#include "OpenXRVk_Traits_Platform.h"
+#include "OpenXRVkTests.h"
+
+void OpenXRVkTest::SetupInternal()
+{
+
+}
+
+void OpenXRVkTest::TearDownInternal()
+{
+
+}
+
+

+ 2 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/OpenXRVk_Traits_Linux.h

@@ -6,3 +6,5 @@
  *
  */
 #pragma once
+
+#define O3DE_TRAIT_DISABLE_FAILED_OPENXRVK_TESTS

+ 1 - 1
Gems/OpenXRVk/Code/Source/Platform/Linux/PAL_linux.cmake

@@ -6,4 +6,4 @@
 #
 #
 
-set(PAL_TRAIT_OPENXRVK_SUPPORTED TRUE)
+set(PAL_TRAIT_OPENXRVK_SUPPORTED FALSE)

+ 4 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/platform_private_linux_files.cmake

@@ -9,4 +9,8 @@
 set(FILES
     OpenXRVk_Traits_Linux.h
     OpenXRVk_Traits_Platform.h
+    ../Common/Default/InputDeviceXRController_Default.cpp
+    ../Common/Default/OculusTouch_Default.cpp
+    ../Common/Default/OculusTouch_Default.h
+    ../Common/Unimplemented/OpenXRVkCommon_Unimplemented.cpp
 )

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/Linux/platform_test_linux_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
+    ../Common/Unimplemented/OpenXRVkTest_Unimplemented.cpp
+)

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/Mac/OpenXRVk_Traits_Mac.h

@@ -0,0 +1,10 @@
+/*
+ * 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
+
+#define O3DE_TRAIT_DISABLE_FAILED_OPENXRVK_TESTS

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/Mac/OpenXRVk_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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 <OpenXRVk_Traits_Mac.h>

+ 14 - 0
Gems/OpenXRVk/Code/Source/Platform/Mac/platform_private_mac_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
+    OpenXRVk_Traits_Mac.h
+    OpenXRVk_Traits_Platform.h
+    ../Common/Unimplemented/InputDeviceXRController_Unimplemented.cpp
+    ../Common/Unimplemented/OpenXRVkCommon_Unimplemented.cpp
+)

+ 8 - 0
Gems/OpenXRVk/Code/Source/Platform/Mac/platform_private_static_mac.cmake

@@ -0,0 +1,8 @@
+#
+# 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
+#
+#
+

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/Mac/platform_test_mac_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
+    ../Common/Unimplemented/OpenXRVkTest_Unimplemented.cpp
+)

+ 2 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/OpenXRVk_Traits_Windows.h

@@ -6,3 +6,5 @@
  *
  */
 #pragma once
+
+#define O3DE_TRAIT_DISABLE_FAILED_OPENXRVK_TESTS

+ 4 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/platform_private_windows_files.cmake

@@ -9,4 +9,8 @@
 set(FILES
     OpenXRVk_Traits_Windows.h
     OpenXRVk_Traits_Platform.h
+    ../Common/Default/InputDeviceXRController_Default.cpp
+    ../Common/Default/OculusTouch_Default.cpp
+    ../Common/Default/OculusTouch_Default.h
+    ../Common/Unimplemented/OpenXRVkCommon_Unimplemented.cpp
 )

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/Windows/platform_test_windows_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
+    ../Common/Unimplemented/OpenXRVkTest_Unimplemented.cpp
+)

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/iOS/OpenXRVk_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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 <OpenXRVk_Traits_iOS.h>

+ 10 - 0
Gems/OpenXRVk/Code/Source/Platform/iOS/OpenXRVk_Traits_iOS.h

@@ -0,0 +1,10 @@
+/*
+ * 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
+
+#define O3DE_TRAIT_DISABLE_FAILED_OPENXRVK_TESTS

+ 14 - 0
Gems/OpenXRVk/Code/Source/Platform/iOS/platform_private_ios_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
+    OpenXRVk_Traits_iOS.h
+    OpenXRVk_Traits_Platform.h
+    ../Common/Unimplemented/InputDeviceXRController_Unimplemented.cpp
+    ../Common/Unimplemented/OpenXRVkCommon_Unimplemented.cpp
+)

+ 8 - 0
Gems/OpenXRVk/Code/Source/Platform/iOS/platform_private_static_ios.cmake

@@ -0,0 +1,8 @@
+#
+# 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
+#
+#
+

+ 11 - 0
Gems/OpenXRVk/Code/Source/Platform/iOS/platform_test_ios_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
+    ../Common/Unimplemented/OpenXRVkTest_Unimplemented.cpp
+)

+ 166 - 0
Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.cpp

@@ -0,0 +1,166 @@
+/*
+ * 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 <XRCameraMovementComponent.h>
+
+#include <AzCore/Component/TransformBus.h>
+#include <AzCore/Serialization/SerializeContext.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/RTTI/BehaviorContext.h>
+
+#include <OpenXRVk/InputDeviceXRController.h>
+
+#include <Atom/RPI.Public/ViewProviderBus.h>
+#include <Atom/RPI.Public/View.h>
+#include <AzFramework/Components/CameraBus.h>
+
+#include <Atom/RPI.Public/ViewportContext.h>
+#include <Atom/RPI.Public/ViewportContextBus.h>
+
+
+namespace OpenXRVk
+{
+    static AZ::Transform GetCameraTransformFromCurrentView()
+    {
+        if (const auto viewportContextMgr = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
+            viewportContextMgr != nullptr)
+        {
+            if (const AZ::RPI::ViewportContextPtr viewportContext = viewportContextMgr->GetDefaultViewportContext();
+                viewportContext != nullptr)
+            {
+                if (const AZ::RPI::ViewPtr view = viewportContext->GetDefaultView();
+                    view != nullptr)
+                {
+                    return view->GetCameraTransform();
+                }
+            }
+        }
+        return AZ::Transform::CreateIdentity();
+    }
+
+    void XRCameraMovementComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<XRCameraMovementComponent, AZ::Component>()
+                ->Version(1)
+                ->Field("Move Speed", &XRCameraMovementComponent::m_moveSpeed)
+                ->Field("Movement Sensitivity", &XRCameraMovementComponent::m_movementSensitivity)
+                ;
+
+            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
+            {
+                editContext->Class<XRCameraMovementComponent>("XR Camera Movement", "Provides XR controller input to control the camera")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                    ->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
+                    ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Component_Placeholder.svg")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &XRCameraMovementComponent::m_moveSpeed, "Move Speed", "Speed of camera movement")
+                        ->Attribute(AZ::Edit::Attributes::Min, 1.f)
+                        ->Attribute(AZ::Edit::Attributes::Max, 50.f)
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &XRCameraMovementComponent::m_movementSensitivity, "Move Sensitivity", "Fine movement sensitivity factor")
+                        ->Attribute(AZ::Edit::Attributes::Min, 0.f)
+                        ->Attribute(AZ::Edit::Attributes::Max, 1.f)
+                    ;
+            }
+        }
+
+        if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
+        {
+            behaviorContext->Class<XRCameraMovementComponent>("XRCameraMovement Component Group")
+                ->Attribute(AZ::Script::Attributes::Category, "OpenXRVk Gem Group")
+                ;
+        }
+    }
+
+    void XRCameraMovementComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        provided.push_back(AZ_CRC_CE("CameraMovementService"));
+    }
+
+    void XRCameraMovementComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        incompatible.push_back(AZ_CRC_CE("CameraMovementService"));
+    }
+
+    void XRCameraMovementComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        required.push_back(AZ_CRC_CE("TransformService"));
+    }
+
+    void XRCameraMovementComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+    }
+
+    void XRCameraMovementComponent::Activate()
+    {
+        AzFramework::InputChannelEventListener::Connect();
+        AZ::TickBus::Handler::BusConnect();
+    }
+
+    void XRCameraMovementComponent::Deactivate()
+    {
+        AZ::TickBus::Handler::BusDisconnect();
+        AzFramework::InputChannelEventListener::Disconnect();
+    }
+
+    void XRCameraMovementComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
+    {
+        AZ::Transform cameraTransform = GetCameraTransformFromCurrentView();
+
+        // Update movement...
+        const float moveSpeed = m_moveSpeed * deltaTime;
+        const AZ::Vector3 movementVec = (cameraTransform.GetBasisX() * m_movement.GetX())
+            + (cameraTransform.GetBasisY() * m_movement.GetY())
+            + (AZ::Vector3{0.f, 0.f, 1.f} * m_movement.GetZ()); // use a fixed UP for the Z direction
+        const AZ::Vector3 newPosition{ (cameraTransform.GetTranslation() + (movementVec * moveSpeed)) };
+        cameraTransform.SetTranslation(newPosition);
+
+        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.
+
+        // 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);
+        }
+    }
+
+} // namespace OpenXRVk

+ 59 - 0
Gems/OpenXRVk/Code/Source/XRCameraMovementComponent.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 <AzCore/Component/Component.h>
+#include <AzCore/Component/TickBus.h>
+#include <AzFramework/Input/Events/InputChannelEventListener.h>
+
+
+namespace OpenXRVk
+{
+    //! XRCameraMovementComponent integrates XR Controller inputs 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
+        : public AZ::Component
+        , public AZ::TickBus::Handler
+        , public AzFramework::InputChannelEventListener
+    {
+    public:
+        AZ_COMPONENT(OpenXRVk::XRCameraMovementComponent, "{7FEC0A04-D994-445C-B8DE-190D03BC3820}");
+
+        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:
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+
+        // AZ::TickBus::Handler
+        void OnTick(float deltaTime, AZ::ScriptTimePoint timePoint) override;
+
+        // AzFramework::InputChannelEventListener
+        bool OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel) override;
+
+    private:
+        void OnXRControllerEvent(const AzFramework::InputChannel& inputChannel);
+
+        // Transient data...
+        AZ::Vector3 m_movement = AZ::Vector3::CreateZero();
+        float m_heightZ = 0.f;
+
+        // Serialized data...
+        float m_moveSpeed = 20.f;
+        float m_movementSensitivity = 0.025f;
+    };
+
+} // namespace OpenXRVk

+ 40 - 0
Gems/OpenXRVk/Code/Tests/OpenXRVkTests.cpp

@@ -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
+ *
+ */
+
+#include <AzTest/AzTest.h>
+#include "OpenXRVkTests.h"
+
+void OpenXRVkTest::SetUp()
+{
+    SetupInternal();
+}
+
+void OpenXRVkTest::TearDown()
+{
+    TearDownInternal();
+}
+
+#ifndef O3DE_TRAIT_DISABLE_ALL_OPENXRVK_TESTS
+
+TEST_F(OpenXRVkTest, PassThisTest)
+{
+    EXPECT_TRUE(true);
+}
+
+#ifdef O3DE_TRAIT_DISABLE_FAILED_OPENXRVK_TESTS
+TEST_F(OpenXRVkTest, DISABLED_ExpectTrue)
+#else
+TEST_F(OpenXRVkTest, ExpectTrue)
+#endif // O3DE_TRAIT_DISABLE_FAILED_OPENXRVK_TESTS
+{
+    EXPECT_TRUE(false);
+}
+
+#endif // !O3DE_TRAIT_DISABLE_ALL_OPENXRVK_TESTS
+
+AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);

+ 24 - 0
Gems/OpenXRVk/Code/Tests/OpenXRVkTests.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 <AzTest/AzTest.h>
+#include <OpenXRVk_Traits_Platform.h>
+
+class OpenXRVkTest
+    : public ::testing::Test
+{
+protected:
+    void SetUp() override;
+    void TearDown() override;
+
+    void SetupInternal();
+    void TearDownInternal();
+};
+

+ 5 - 0
Gems/OpenXRVk/Code/openxrvk_private_common_files.cmake

@@ -7,6 +7,7 @@
 #
 
 set(FILES
+    Include/OpenXRVk/InputDeviceXRController.h
     Include/OpenXRVk/OpenXRVkDevice.h
     Include/OpenXRVk/OpenXRVkInput.h
     Include/OpenXRVk/OpenXRVkInstance.h
@@ -16,6 +17,8 @@ set(FILES
     Include/OpenXRVk/OpenXRVkSwapChain.h
     Include/OpenXRVk/OpenXRVkSystemComponent.h
     Include/OpenXRVk/OpenXRVkUtils.h
+    Source/InputDeviceXRController.cpp
+    Source/OpenXRVkCommon.h
     Source/OpenXRVkDevice.cpp
     Source/OpenXRVkInput.cpp
     Source/OpenXRVkInstance.cpp
@@ -25,4 +28,6 @@ set(FILES
     Source/OpenXRVkSwapChain.cpp
     Source/OpenXRVkSystemComponent.cpp
     Source/OpenXRVkUtils.cpp
+    Source/XRCameraMovementComponent.cpp
+    Source/XRCameraMovementComponent.h
 )

+ 12 - 0
Gems/OpenXRVk/Code/openxrvk_tests_files.cmake

@@ -0,0 +1,12 @@
+#
+# 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
+    Tests/OpenXRVkTests.cpp
+    Tests/OpenXRVkTests.h
+)

+ 7 - 0
Gems/OpenXRVk/External/OculusOpenXRMobileSDK/README.md

@@ -0,0 +1,7 @@
+# Oculus OpenXR Mobile SDK
+
+The Oculus OpenXR Mobile SDK is not included as part of O3DE.
+
+When enabling OpenXRVk Gem, download the SDK and uncompress it in the following folder within the gem: `OpenXRVk\External\OculusOpenXRMobileSDK`
+
+The Oculus OpenXR Mobile SDK can be found in the following link: https://developer.oculus.com/downloads/native-android/

+ 31 - 1
Gems/XR/Code/CMakeLists.txt

@@ -70,6 +70,7 @@ ly_add_target(
             ${pal_source_dir}
         PUBLIC
             Include
+            ${pal_include_dir}
     BUILD_DEPENDENCIES
         PRIVATE
             Gem::XR.Static
@@ -77,4 +78,33 @@ ly_add_target(
 
 # use the XR module in clients and tools:
 ly_create_alias(NAME XR.Clients NAMESPACE Gem TARGETS Gem::XR)
-ly_create_alias(NAME XR.Tools NAMESPACE Gem TARGETS Gem::XR)
+
+if(PAL_TRAIT_BUILD_HOST_TOOLS)
+    ly_create_alias(NAME XR.Tools NAMESPACE Gem TARGETS Gem::XR)
+endif()
+
+################################################################################
+# Tests
+################################################################################
+if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
+    ly_add_target(
+        NAME XR.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
+        NAMESPACE Gem
+        OUTPUT_NAME XR.Tests
+        FILES_CMAKE
+            xr_tests_files.cmake
+            ${pal_source_dir}/platform_test_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
+        INCLUDE_DIRECTORIES
+            PRIVATE
+                Tests
+                Source
+                ${pal_source_dir}
+        BUILD_DEPENDENCIES
+            PRIVATE
+                AZ::AzTest
+                Gem::XR.Static
+    )
+    ly_add_googletest(
+        NAME Gem::XR.Tests
+    )
+endif()

+ 5 - 4
Gems/XR/Code/Include/XR/XRBase.h

@@ -11,6 +11,7 @@
 #include <AzCore/std/smart_ptr/intrusive_ptr.h>
 #include <AzCore/std/string/string.h>
 #include <AzCore/std/containers/vector.h>
+#include <Atom/RHI.Reflect/Base.h>      // For AZ::RHI::ResultCode
 
 namespace XR
 {
@@ -22,17 +23,17 @@ namespace XR
 
     using StringList = AZStd::vector<AZStd::string>;
     using RawStringList = AZStd::vector<const char*>;
-    
+
     enum class  Side : uint32_t
     {
         Left = 0,
         Right,
         Count
-    }; 
+    };
 
 #define RETURN_RESULTCODE_IF_UNSUCCESSFUL(result) \
-    if (result != AZ::RHI::ResultCode::Success) {\
-        return result;\
+    if ((result) != AZ::RHI::ResultCode::Success) {\
+        return (result);\
     }
 }
 

+ 23 - 12
Gems/XR/Code/Include/XR/XRDevice.h

@@ -18,8 +18,9 @@
 
 namespace XR
 {
-    //! Base XR device class which will provide access to the back-end concrete object
     class Session;
+
+    //! Base XR device class which will provide access to the back-end concrete object
     class Device
         : public XR::Object
     {
@@ -28,7 +29,7 @@ namespace XR
         AZ_RTTI(Device, "{A31B0DC2-BD54-443E-9350-EB1B10670FF9}");
 
         Device() = default;
-        virtual ~Device() = default;
+        ~Device() override = default;
 
         struct Descriptor
         {
@@ -38,31 +39,37 @@ namespace XR
 
         //! Create the xr specific native device object and populate the XRDeviceDescriptor with it.
         virtual AZ::RHI::ResultCode InitDeviceInternal(AZ::RHI::XRDeviceDescriptor* instanceDescriptor) = 0;
-        
+
         //! Returns true if rendering data is valid for the current frame.
         virtual bool ShouldRender() const = 0;
-        
+
         //! Returns fov data for a give view index.
-        virtual AZ::RPI::FovData GetViewFov(AZ::u32 viewIndex) const = 0;
+        virtual AZ::RHI::ResultCode GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const = 0;
 
         //! Returns pose data for a give view index.
-        virtual AZ::RPI::PoseData GetViewPose(AZ::u32 viewIndex) const = 0;
+        virtual AZ::RHI::ResultCode GetViewPose(AZ::u32 viewIndex, AZ::RPI::PoseData& outPoseData) const = 0;
 
         //! Init the XR device.
         AZ::RHI::ResultCode Init(Descriptor descriptor);
-        
+
         //! Signal Begin frame to the underlying back end.
+        //! @note This function is called from the thread related to the presentation queue.
         bool BeginFrame();
 
         //! Signal End frame to the underlying back end.
+        //! @note This function is called from the thread related to the presentation queue.
         void EndFrame(Ptr<SwapChain>);
 
+        //! Signal after Endframe has been executed to the underlying back end.
+        //! @note This function is called from the main thread.
+        void PostFrame();
+
         //! Signal the back-end to acquire swapchain images.
         bool AcquireSwapChainImage(AZ::u32 viewIndex, SwapChain* swapChain);
 
         //! Register XR session with the device.
         void RegisterSession(Ptr<Session> session);
-    
+
         //! UnRegister XR session with the device.
         void UnRegisterSession();
 
@@ -73,22 +80,26 @@ namespace XR
         Ptr<Session> GetSession() const;
 
     protected:
-    
         //! Called when the device is being shutdown.
         virtual void ShutdownInternal() = 0;
 
         //! Called when the device is beginning a frame for processing.
+        //! @note This function is called from the thread related to the presentation queue.
         virtual bool BeginFrameInternal() = 0;
 
-        //! Called when the device is ending a frame for processing. 
+        //! Called when the device is ending a frame for processing.
         //! Pass in the active swapchain in order to allow the back end to release the swap chain images
+        //! @note This function is called from the thread related to the presentation queue.
         virtual void EndFrameInternal(XR::Ptr<XR::SwapChain>) = 0;
 
+        //! Called after the EndFrame has been executed.
+        //! @note This function is called from the main thread.
+        virtual void PostFrameInternal() = 0;
+
         //! Called when the device is beginning a frame for processing.
         virtual bool AcquireSwapChainImageInternal(AZ::u32 viewIndex, XR::SwapChain* baseSwapChain) = 0;
 
     private:
-
         ///////////////////////////////////////////////////////////////////
         // XR::Object
         void Shutdown() override;
@@ -97,4 +108,4 @@ namespace XR
         Ptr<Session> m_session;
         Descriptor m_descriptor;
     };
-}
+}

+ 4 - 7
Gems/XR/Code/Include/XR/XRInput.h

@@ -13,9 +13,11 @@
 #include <XR/XRObject.h>
 
 namespace XR
-{ 
+{
     class Session;
     class Instance;
+    class Device;
+
     // This class will be responsible for creating XR::Input
     // which manage event queue or poll actions
     class Input
@@ -25,21 +27,16 @@ namespace XR
         AZ_CLASS_ALLOCATOR(Input, AZ::SystemAllocator, 0);
         AZ_RTTI(Input, "{DCDFC6A7-B457-414B-BC24-0831C2AC628B}");
 
-        Input() = default;
-        virtual ~Input() = default;
-        
         struct Descriptor
         {
             Ptr<Instance> m_instance;
             Ptr<Device> m_device;
             Ptr<Session> m_session;
         };
-        
+
         AZ::RHI::ResultCode Init(Descriptor descriptor);
         const Descriptor& GetDescriptor() const;
 
-        virtual void PollActions() = 0;
-       
     private:
         ///////////////////////////////////////////////////////////////////
         // XR::Object

+ 12 - 12
Gems/XR/Code/Include/XR/XRInstance.h

@@ -24,10 +24,10 @@ namespace XR
         AZ_RTTI(Instance, "{1C457924-56A4-444F-BC72-4D31A097BA70}");
 
         Instance() = default;
-        virtual ~Instance() = default;
+        ~Instance() override = default;
 
-        //! Init the back-end instance. It is responsible for figuring out supported layers and extensions 
-        //! and based on that a xr instance is created. It also has logging support based on validation mode.  
+        //! Init the back-end instance. It is responsible for figuring out supported layers and extensions
+        //! and based on that a xr instance is created. It also has logging support based on validation mode.
         AZ::RHI::ResultCode Init(AZ::RHI::ValidationMode validationMode);
 
         //! API to init the native instance object and populate the XRInstanceDecriptor with it.
@@ -39,21 +39,21 @@ namespace XR
         //! API to retrieve the native physical device for a specific index.
         virtual AZ::RHI::ResultCode GetXRPhysicalDevice(AZ::RHI::XRPhysicalDeviceDescriptor* physicalDeviceDescriptor, int32_t index) = 0;
 
-    private:
-
-        ///////////////////////////////////////////////////////////////////
-        // XR::Object
-        void Shutdown() override;
-        ///////////////////////////////////////////////////////////////////
+    protected:
+        //! API to allow backend object to initialize native xr instance.
+        virtual AZ::RHI::ResultCode InitInstanceInternal() = 0;
 
         //! Called when the XR instance is being shutdown.
         virtual void ShutdownInternal() = 0;
 
-        //! API to allow backend object to initialize native xr instance. 
-        virtual AZ::RHI::ResultCode InitInstanceInternal(AZ::RHI::ValidationMode m_validationMode) = 0;
-       
         //Cache validation mode in case the backend object needs to use it.
         AZ::RHI::ValidationMode m_validationMode = AZ::RHI::ValidationMode::Disabled;
+
+    private:
+        ///////////////////////////////////////////////////////////////////
+        // XR::Object
+        void Shutdown() override;
+        ///////////////////////////////////////////////////////////////////
     };
 
 } // namespace XR

+ 1 - 1
Gems/XR/Code/Include/XR/XRObject.h

@@ -31,7 +31,7 @@ namespace XR
     {
     public:
         AZ_RTTI(Object, "{74FCA8BF-CBDA-43EB-A378-89F752ED2FCA}");
-        virtual ~Object() = default;
+        ~Object() override = default;
 
         //! Sets the name of the object.
         void SetName(const AZ::Name& name)

+ 36 - 7
Gems/XR/Code/Include/XR/XRSession.h

@@ -36,8 +36,8 @@ namespace XR
         };
 
         Session() = default;
-        virtual ~Session() = default;
-        
+        ~Session() override = default;
+
         //! Initialize the XrSession object which is responsible for creating
         //! XrInput and XrSpace
         AZ::RHI::ResultCode Init(const Descriptor& descriptor);
@@ -50,7 +50,7 @@ namespace XR
 
         //! Get the Xr Space object
         Space* GetSpace() const;
-        
+
         //! Return true if session is running
         virtual bool IsSessionRunning() const = 0;
 
@@ -59,7 +59,7 @@ namespace XR
 
         //! Return true if a restart is requested
         virtual bool IsRestartRequested() const = 0;
-    
+
         //! Return true if render loop skip is requested
         virtual bool IsExitRenderLoopRequested() const = 0;
 
@@ -73,16 +73,45 @@ namespace XR
         virtual void LocateControllerSpace(AZ::u32 handIndex) = 0;
 
         //! Api to retrieve the controller space data
-        virtual AZ::RPI::PoseData GetControllerPose(AZ::u32 handIndex) const = 0;
+        virtual AZ::RHI::ResultCode GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const = 0;
+
+        //! Api to retrieve the controller space data associated with local view translated and rotated by 60 deg left or right based on handIndex
+        virtual AZ::RHI::ResultCode GetControllerStagePose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const = 0;
 
         //! Api to retrieve the controller scale data
         virtual float GetControllerScale(AZ::u32 handIndex) const = 0;
 
         //! Api to retrieve the front view space data
-        virtual AZ::RPI::PoseData GetViewFrontPose() const = 0;
+        virtual AZ::RHI::ResultCode GetViewFrontPose(AZ::RPI::PoseData& outPoseData) const = 0;
 
-    private:
+        //! Api to retrieve the local view space data
+        virtual AZ::RHI::ResultCode GetViewLocalPose(AZ::RPI::PoseData& outPoseData) const = 0;
+
+        //! Api to retrieve the controller X button state
+        virtual float GetXButtonState() const = 0;
+
+        //! Api to retrieve the controller Y button state
+        virtual float GetYButtonState() const = 0;
 
+        //! Api to retrieve the controller A button state
+        virtual float GetAButtonState() const = 0;
+
+        //! Api to retrieve the controller B button state
+        virtual float GetBButtonState() const = 0;
+
+        //! Api to retrieve the joystick controller state related to x-axis
+        virtual float GetXJoyStickState(AZ::u32 handIndex) const = 0;
+
+        //! Api to retrieve the joystick controller state related to y-axis
+        virtual float GetYJoyStickState(AZ::u32 handIndex) const = 0;
+
+        //! Api to retrieve the controller Y button state
+        virtual float GetSqueezeState(AZ::u32 handIndex) const = 0;
+
+        //! Api to retrieve the controller Y button state
+        virtual float GetTriggerState(AZ::u32 handIndex) const = 0;
+
+    private:
         ///////////////////////////////////////////////////////////////////
         // XR::Object
         void Shutdown() override;

+ 2 - 3
Gems/XR/Code/Include/XR/XRSpace.h

@@ -18,7 +18,6 @@ namespace XR
     class Space
         : public ::XR::Object
     {
-	    
     public:
         AZ_CLASS_ALLOCATOR(Space, AZ::SystemAllocator, 0);
         AZ_RTTI(Space, "{A78A37F1-8861-4EB4-8FC6-0E9C11394EF1}");
@@ -30,8 +29,8 @@ namespace XR
 
         AZ::RHI::ResultCode Init(Descriptor descriptor);
         const Space::Descriptor& GetDescriptor() const;
-    private:
 
+    private:
         ///////////////////////////////////////////////////////////////////
         // XR::Object
         void Shutdown() override;
@@ -40,7 +39,7 @@ namespace XR
         //! Called when the XR instance is being shutdown.
         virtual void ShutdownInternal() = 0;
         virtual AZ::RHI::ResultCode InitInternal() = 0;
-       
+
         Descriptor m_descriptor;
     };
 } // namespace XR

+ 16 - 14
Gems/XR/Code/Include/XR/XRSwapChain.h

@@ -11,7 +11,7 @@
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/std/containers/vector.h>
 #include <AzCore/std/smart_ptr/intrusive_ptr.h>
-#include <Atom/RHI/XRRenderingInterface.h>
+#include <Atom/RHI.Reflect/Format.h>
 #include <XR/XRObject.h>
 
 namespace XR
@@ -22,14 +22,14 @@ namespace XR
     //! This class will be responsible for creating multiple XR::SwapChain::ViewSwapchains
     //! (one per view). Each XR::SwapChain::ViewSwapchain will then be responsible
     //! for manging and synchronizing multiple swap chain images
-    class SwapChain 
+    class SwapChain
         : public XR::Object
     {
     public:
         AZ_CLASS_ALLOCATOR(SwapChain, AZ::SystemAllocator, 0);
         AZ_RTTI(SwapChain, "{0C666E76-E4B7-4097-8D14-713DC2C446EF}");
 
-        class Image 
+        class Image
             : public AZStd::intrusive_base
         {
         public:
@@ -37,10 +37,10 @@ namespace XR
             AZ_RTTI(Image, "{4037835D-F1BB-4407-BC98-2299CC7BE0A3}");
 
             Image() = default;
-            virtual ~Image() = default;
+            ~Image() override = default;
         };
 
-        class View 
+        class View
             : public AZStd::intrusive_base
         {
         public:
@@ -48,7 +48,7 @@ namespace XR
             AZ_RTTI(View, "{774EB724-8261-4684-AA78-EDF6BBECD48A}");
 
             View() = default;
-            virtual ~View() = default;
+            ~View() override = default;
 
             virtual void Shutdown() = 0;
             virtual AZ::u32 GetCurrentImageIndex() const = 0;
@@ -66,7 +66,7 @@ namespace XR
 
             //! Width of the swap chain view.
             AZ::u32 m_width = 0;
-    
+
             //! Height of the swap chain view.
             AZ::u32 m_height = 0;
         };
@@ -80,7 +80,7 @@ namespace XR
         };
 
         //! Returns the view swap chain related to the index.
-        SwapChain::View* GetView(const AZ::u32 swapChainIndex) const;
+        SwapChain::View* GetView(AZ::u32 swapChainIndex) const;
 
         //! Returns the image associated with the provided image
         //! index and view swap chain index.
@@ -89,7 +89,7 @@ namespace XR
         //! Initialize the XR swapchain.
         AZ::RHI::ResultCode Init(const Descriptor& descriptor);
 
-        //! Get the descriptor. 
+        //! Get the descriptor.
         const Descriptor& GetDescriptor() const;
 
         //! Get the number of Xr views
@@ -97,21 +97,23 @@ namespace XR
 
         //! Api to allow the back end object to return the requested native swapchain image
         virtual AZ::RHI::ResultCode GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const = 0;
-       
+
         //! Api to allow the back end to report the recommended swapchain width
         virtual AZ::u32 GetSwapChainWidth(AZ::u32 viewIndex) const = 0;
-        
+
         //! Api to allow the back end to report the recommended swapchain height
         virtual AZ::u32 GetSwapChainHeight(AZ::u32 viewIndex) const = 0;
 
+        //! Api to allow the back end to report the swapchain format.
+        virtual AZ::RHI::Format GetSwapChainFormat(AZ::u32 viewIndex) const = 0;
+
     protected:
-        
         //! Number of Xr views
         AZ::u32 m_numViews = 0;
 
         //! Vector to hold all the SwapChain View objects
         AZStd::vector<Ptr<SwapChain::View>> m_viewSwapchains;
-   
+
     private:
         ///////////////////////////////////////////////////////////////////
         // XR::Object override
@@ -121,7 +123,7 @@ namespace XR
         //! Called when the swapchain is being shutdown.
         virtual void ShutdownInternal() = 0;
 
-        //! Api to allow the back end object to initialize  
+        //! Api to allow the back end object to initialize
         virtual AZ::RHI::ResultCode InitInternal() = 0;
 
         Descriptor m_descriptor;

+ 28 - 14
Gems/XR/Code/Include/XR/XRSystem.h

@@ -21,9 +21,9 @@
 
 namespace XR
 {
-    //! This class is the window to everything XR related. It implements 
-    //! RPI::RenderingInterface and RHI::RenderingInterface but
-    //! can be extended to implement other non rendering interfaces if needed. 
+    //! This class is the window to everything XR related.
+    //! It implements RPI::RenderingInterface and RHI::RenderingInterface but
+    //! can be extended to implement other non rendering interfaces if needed.
     class System
         : public AZ::RPI::XRRenderingInterface
         , public AZ::RHI::XRRenderingInterface
@@ -34,10 +34,9 @@ namespace XR
         AZ_CLASS_ALLOCATOR(System, AZ::SystemAllocator, 0);
         AZ_RTTI(System, "{C3E0291D-FB30-4E27-AB0D-14606A8C3C1F}");
 
-        AZ_DISABLE_COPY_MOVE(System);
-
         System() = default;
-        ~System() = default;
+        ~System() override = default;
+        AZ_DISABLE_COPY_MOVE(System);
 
         struct Descriptor
         {
@@ -47,7 +46,7 @@ namespace XR
         //! Init the XRSystem.
         void Init(const Descriptor& descriptor);
 
-        //! Destroy any relevant objects held by this .class 
+        //! Destroy any relevant objects held by this .class
         void Shutdown();
 
         //! Handle XR events and actions
@@ -61,15 +60,27 @@ namespace XR
         AZ::RHI::ResultCode GetSwapChainImage(AZ::RHI::XRSwapChainDescriptor* swapchainDescriptor) const override;
         AZ::u32 GetSwapChainWidth(AZ::u32 viewIndex) const override;
         AZ::u32 GetSwapChainHeight(AZ::u32 viewIndex) const override;
-        AZ::RPI::FovData GetViewFov(AZ::u32 viewIndex) const override;
-        AZ::RPI::PoseData GetViewPose(AZ::u32 viewIndex) const override;
-        AZ::RPI::PoseData GetViewFrontPose() const override;
-        AZ::RPI::PoseData GetControllerPose(AZ::u32 handIndex) const override;
+        AZ::RHI::Format GetSwapChainFormat(AZ::u32 viewIndex) const override;
+        AZ::RHI::ResultCode GetViewFov(AZ::u32 viewIndex, AZ::RPI::FovData& outFovData) const override;
+        AZ::RHI::ResultCode GetViewPose(AZ::u32 viewIndex, AZ::RPI::PoseData& outPoseData) const override;
+        AZ::RHI::ResultCode GetViewFrontPose(AZ::RPI::PoseData& outPoseData) const override;
+        AZ::RHI::ResultCode GetViewLocalPose(AZ::RPI::PoseData& outPoseData) const override;
+        AZ::RHI::ResultCode GetControllerStagePose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const override;
+        AZ::RHI::ResultCode GetControllerPose(AZ::u32 handIndex, AZ::RPI::PoseData& outPoseData) const override;
         float GetControllerScale(AZ::u32 handIndex) const override;
         bool ShouldRender() const override;
-        AZ::Matrix4x4 CreateProjectionOffset(float angleLeft, float angleRight, 
-                                             float angleBottom, float angleTop, 
-                                             float nearDist, float farDist) override;
+        AZ::Matrix4x4 CreateStereoscopicProjection(float angleLeft, float angleRight,
+                                                   float angleBottom, float angleTop,
+                                                   float nearDist, float farDist, bool reverseDepth) override;
+        AZ::RHI::XRRenderingInterface* GetRHIXRRenderingInterface() override;
+        float GetXButtonState() const override;
+        float GetYButtonState() const override;
+        float GetAButtonState() const override;
+        float GetBButtonState() const override;
+        float GetXJoyStickState(AZ::u32 handIndex) const override;
+        float GetYJoyStickState(AZ::u32 handIndex) const override;
+        float GetSqueezeState(AZ::u32 handIndex) const override;
+        float GetTriggerState(AZ::u32 handIndex) const override;
         ///////////////////////////////////////////////////////////////////
 
         ///////////////////////////////////////////////////////////////////
@@ -83,6 +94,9 @@ namespace XR
         AZ::u32 GetCurrentImageIndex(AZ::u32 viewIndex) const override;
         void BeginFrame() override;
         void EndFrame() override;
+        void PostFrame() override;
+        bool IsDefaultRenderPipelineNeeded() const override;
+        bool IsDefaultRenderPipelineEnabledOnHost() const override;
         ///////////////////////////////////////////////////////////////////
 
     private:

+ 2 - 2
Gems/XR/Code/Include/XR/XRSystemComponent.h

@@ -23,8 +23,8 @@ namespace XR
         static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
         static void Reflect(AZ::ReflectContext* context);
 
-        SystemComponent();
-        ~SystemComponent() = default;
+        SystemComponent() = default;
+        ~SystemComponent() override = default;
 
         //////////////////////////////////////////////////////////////////////////
         // Component

+ 8 - 2
Gems/XR/Code/Include/XR/XRUtils.h

@@ -12,6 +12,12 @@
 
 namespace XR
 {
-    //! Creates an off-center projection matrix suitable for VR
-    AZ::Matrix4x4 CreateProjectionOffset(float angleLeft, float angleRight, float angleBottom, float angleTop, float nearDist, float farDist);
+    //! Creates an off-center projection matrix suitable for VR.
+    //! It does the following in order to provide a stereoscopic projection:
+    //! Stretch more horizontally and vertically
+    //! Generate asymmetric or off-center projection matrix
+    //! Right-handed coord system as OpenXR provides data in that system
+    //! Provides support for reverse depth in case we want better depth precision
+    //! Handles the case where farDist is less than nearDist whereby it will place far plane at infinity.
+    AZ::Matrix4x4 CreateStereoscopicProjection(float angleLeft, float angleRight, float angleBottom, float angleTop, float nearDist, float farDist, bool reverseDepth);
 }

+ 1 - 0
Gems/XR/Code/Source/Platform/Android/XR_Traits_Android.h

@@ -7,3 +7,4 @@
  */
 #pragma once
 
+#define O3DE_TRAIT_DISABLE_FAILED_XR_TESTS

+ 11 - 0
Gems/XR/Code/Source/Platform/Android/platform_test_android_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
+    ../Common/Unimplemented/XRTest_Unimplemented.cpp
+)

+ 24 - 0
Gems/XR/Code/Source/Platform/Common/Unimplemented/XRTest_Unimplemented.cpp

@@ -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
+ *
+ */
+
+#include <AzTest/AzTest.h>
+
+#include "XR_Traits_Platform.h"
+#include "XRTest.h"
+
+void XRTest::SetupInternal()
+{
+
+}
+
+void XRTest::TearDownInternal()
+{
+
+}
+
+

+ 1 - 1
Gems/XR/Code/Source/Platform/Linux/PAL_linux.cmake

@@ -6,4 +6,4 @@
 #
 #
 
-set(PAL_TRAIT_XR_SUPPORTED TRUE)
+set(PAL_TRAIT_XR_SUPPORTED FALSE)

+ 1 - 0
Gems/XR/Code/Source/Platform/Linux/XR_Traits_Linux.h

@@ -7,3 +7,4 @@
  */
 #pragma once
 
+#define O3DE_TRAIT_DISABLE_FAILED_XR_TESTS

+ 11 - 0
Gems/XR/Code/Source/Platform/Linux/platform_test_linux_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
+    ../Common/Unimplemented/XRTest_Unimplemented.cpp
+)

+ 10 - 0
Gems/XR/Code/Source/Platform/Mac/XR_Traits_Mac.h

@@ -0,0 +1,10 @@
+/*
+ * 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
+
+#define O3DE_TRAIT_DISABLE_FAILED_XR_TESTS

+ 10 - 0
Gems/XR/Code/Source/Platform/Mac/XR_Traits_Platform.h

@@ -0,0 +1,10 @@
+/*
+ * 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 <XR_Traits_Mac.h>

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików