Browse Source

Improved XRExampleComponent (#655)

* Improved XRExampleComponent

The XREampleComponent now uses
AZ::RPI::XRSpaceNotificationBus::OnXRSpaceLocationsChanged()
and also uses the O3DE coordinate space convention.

This improves Jittering, sense of depth and correct orientation
of the cubes used to represent the joystick locations relative to the
headset pose.

Signed-off-by: galibzon <[email protected]>

* Changed required version for both
XR and OpenXRVk Gems to >= 1.0.1

Signed-off-by: galibzon <[email protected]>

---------

Signed-off-by: galibzon <[email protected]>
galibzon 1 year ago
parent
commit
eed182083c
3 changed files with 82 additions and 45 deletions
  1. 59 42
      Gem/Code/Source/RHI/XRExampleComponent.cpp
  2. 21 1
      Gem/Code/Source/RHI/XRExampleComponent.h
  3. 2 2
      project.json

+ 59 - 42
Gem/Code/Source/RHI/XRExampleComponent.cpp

@@ -46,6 +46,7 @@ namespace AtomSampleViewer
         CreateScope();
         AZ::RHI::RHISystemNotificationBus::Handler::BusConnect();
         AZ::TickBus::Handler::BusConnect();
+        AZ::RPI::XRSpaceNotificationBus::Handler::BusConnect();
     }
 
     void XRExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
@@ -53,6 +54,37 @@ namespace AtomSampleViewer
         m_time += deltaTime;
     }
 
+    void XRExampleComponent::OnXRSpaceLocationsChanged(
+        const AZ::Transform& baseSpaceToHeadTm, const AZ::Transform& headToLeftEyeTm, const AZ::Transform& headToRightEyeTm)
+    {
+        AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
+        if (!xrSystem || !xrSystem->ShouldRender())
+        {
+            return;
+        }
+
+        m_baseSpaceToHeadTm = baseSpaceToHeadTm;
+        const AZ::Transform eyeWorldZupTm = (m_viewIndex == 0)
+            ? baseSpaceToHeadTm * headToLeftEyeTm // This is true for both: main pipeline and left eye pipeline.
+            : baseSpaceToHeadTm * headToRightEyeTm; // Right eye pipeline.
+
+        // When rendering, the camera Up vector should be Y+, and the forward vector is Z-
+        static const AZ::Transform zUpToYUp = AZ::Transform::CreateRotationX(AZ::Constants::HalfPi);
+        const auto eyeWorldYupTm = eyeWorldZupTm * zUpToYUp;
+
+        AZ::RPI::FovData fovData;
+        [[maybe_unused]] AZ::RHI::ResultCode resultCode = xrSystem->GetViewFov(m_viewIndex, fovData);
+
+        static const float clip_near = 0.05f;
+        static const float clip_far = 100.0f;
+        bool reverseDepth = false;
+        const auto projectionMat44 = xrSystem->CreateStereoscopicProjection(
+            fovData.m_angleLeft, fovData.m_angleRight, fovData.m_angleDown, fovData.m_angleUp, clip_near, clip_far, reverseDepth);
+
+        AZ::Matrix4x4 viewMat = AZ::Matrix4x4::CreateFromTransform(eyeWorldYupTm.GetInverse());
+        m_viewProjMatrix = projectionMat44 * viewMat;
+    }
+
     void XRExampleComponent::OnFramePrepare(AZ::RHI::FrameGraphBuilder& frameGraphBuilder)
     {
         AZ::Matrix4x4 projection = AZ::Matrix4x4::CreateIdentity();
@@ -60,39 +92,22 @@ namespace AtomSampleViewer
         AZ::RPI::XRRenderingInterface* xrSystem = AZ::RPI::RPISystemInterface::Get()->GetXRSystem();
         if (xrSystem && xrSystem->ShouldRender())
         {
-            AZ::RPI::FovData fovData;
-            AZ::RPI::PoseData poseData, frontViewPoseData;
-            [[maybe_unused]] AZ::RHI::ResultCode resultCode = xrSystem->GetViewFov(m_viewIndex, fovData);
-            resultCode = xrSystem->GetViewPose(m_viewIndex, poseData);
-                
-            static const float clip_near = 0.05f;
-            static const float clip_far = 100.0f;
-            bool reverseDepth = false;
-            projection = xrSystem->CreateStereoscopicProjection(fovData.m_angleLeft, fovData.m_angleRight,
-                                                          fovData.m_angleDown, fovData.m_angleUp, 
-                                                          clip_near, clip_far, reverseDepth);
-
-            AZ::Quaternion poseOrientation = poseData.m_orientation; 
-            poseOrientation.InvertFast(); 
-            AZ::Matrix4x4 viewMat = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(poseOrientation, -poseData.m_position);
-            m_viewProjMatrix = projection * viewMat;
- 
             const AZ::Matrix4x4 initialScaleMat = AZ::Matrix4x4::CreateScale(AZ::Vector3(0.1f, 0.1f, 0.1f));
 
+            AZ::RPI::PoseData frontViewPoseData;
             //Model matrix for the cube related to the front view
-            resultCode = xrSystem->GetViewFrontPose(frontViewPoseData);
-            m_modelMatrices[0] = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(frontViewPoseData.m_orientation, frontViewPoseData.m_position) * initialScaleMat;
-                      
-            //Model matrix for the cube related to the left controller
-            AZ::RPI::PoseData controllerLeftPose, controllerRightPose;
-            resultCode = xrSystem->GetControllerPose(0, controllerLeftPose);
-            AZ::Matrix4x4 leftScaleMat = initialScaleMat * AZ::Matrix4x4::CreateScale(AZ::Vector3(xrSystem->GetControllerScale(0)));
-            m_modelMatrices[1] = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(controllerLeftPose.m_orientation, controllerLeftPose.m_position) * leftScaleMat;
-
-            //Model matrix for the cube related to the right controller
-            AZ::Matrix4x4 rightScaleMat = initialScaleMat * AZ::Matrix4x4::CreateScale(AZ::Vector3(xrSystem->GetControllerScale(1)));
-            resultCode = xrSystem->GetControllerPose(1, controllerRightPose);
-            m_modelMatrices[2] = AZ::Matrix4x4::CreateFromQuaternionAndTranslation(controllerRightPose.m_orientation, controllerRightPose.m_position) * rightScaleMat;
+            [[maybe_unused]] AZ::RHI::ResultCode resultCode = xrSystem->GetViewFrontPose(frontViewPoseData);
+            m_modelMatrices[0] =
+                AZ::Matrix4x4::CreateFromQuaternionAndTranslation(frontViewPoseData.m_orientation, frontViewPoseData.m_position) *
+                initialScaleMat;
+
+            AZ::Transform controllerLeftTm;
+            resultCode = xrSystem->GetControllerTransform(0, controllerLeftTm);
+            m_modelMatrices[1] = AZ::Matrix4x4::CreateFromTransform(m_baseSpaceToHeadTm * controllerLeftTm) * initialScaleMat;
+
+            AZ::Transform controllerRightTm;
+            resultCode = xrSystem->GetControllerTransform(1, controllerRightTm);
+            m_modelMatrices[2] = AZ::Matrix4x4::CreateFromTransform(m_baseSpaceToHeadTm * controllerRightTm) * initialScaleMat;
         }      
         
         for (int i = 0; i < NumberOfCubes; ++i)
@@ -109,17 +124,17 @@ namespace AtomSampleViewer
     {
         const AZStd::fixed_vector<AZ::Color, GeometryVertexCount> vertexColor =
         {
-            //Front Face
+            //Top Face
             AZ::Colors::DarkBlue,   AZ::Colors::DarkBlue,   AZ::Colors::DarkBlue,   AZ::Colors::DarkBlue,
-            //Back Face                                                                       
+            //bottom Face                                                                       
             AZ::Colors::Blue,       AZ::Colors::Blue,       AZ::Colors::Blue,       AZ::Colors::Blue,
             //Left Face                                                                      
             AZ::Colors::DarkGreen,  AZ::Colors::DarkGreen,  AZ::Colors::DarkGreen,  AZ::Colors::DarkGreen,
             //Right Face                                                                    
             AZ::Colors::Green,      AZ::Colors::Green,      AZ::Colors::Green,      AZ::Colors::Green,
-            //Top Face                                                                  
+            //Front Face                                                                  
             AZ::Colors::DarkRed,    AZ::Colors::DarkRed,    AZ::Colors::DarkRed,    AZ::Colors::DarkRed,
-            //Bottom Face                                                                    
+            //Back Face                                                                    
             AZ::Colors::Red,        AZ::Colors::Red,        AZ::Colors::Red,        AZ::Colors::Red,
         };
 
@@ -129,17 +144,17 @@ namespace AtomSampleViewer
             
             const AZStd::fixed_vector<AZ::Vector3, GeometryVertexCount> vertices =
             {
-                //Front Face
+                //Top Face
                 AZ::Vector3(1.0, 1.0, 1.0),         AZ::Vector3(-1.0, 1.0, 1.0),     AZ::Vector3(-1.0, -1.0, 1.0),    AZ::Vector3(1.0, -1.0, 1.0),
-                //Back Face                                                                       
+                //Bottom Face                                                                       
                 AZ::Vector3(1.0, 1.0, -1.0),        AZ::Vector3(-1.0, 1.0, -1.0),    AZ::Vector3(-1.0, -1.0, -1.0),   AZ::Vector3(1.0, -1.0, -1.0),
                 //Left Face                                                                      
                 AZ::Vector3(-1.0, 1.0, 1.0),        AZ::Vector3(-1.0, -1.0, 1.0),    AZ::Vector3(-1.0, -1.0, -1.0),   AZ::Vector3(-1.0, 1.0, -1.0),
                 //Right Face                                                                    
                 AZ::Vector3(1.0, 1.0, 1.0),         AZ::Vector3(1.0, -1.0, 1.0),     AZ::Vector3(1.0, -1.0, -1.0),    AZ::Vector3(1.0, 1.0, -1.0),
-                //Top Face                                                                  
+                //Front Face                                                                  
                 AZ::Vector3(1.0, 1.0, 1.0),         AZ::Vector3(-1.0, 1.0, 1.0),     AZ::Vector3(-1.0, 1.0, -1.0),    AZ::Vector3(1.0, 1.0, -1.0),
-                //Bottom Face                                                                    
+                //Back Face                                                                    
                 AZ::Vector3(1.0, -1.0, 1.0),        AZ::Vector3(-1.0, -1.0, 1.0),    AZ::Vector3(-1.0, -1.0, -1.0),   AZ::Vector3(1.0, -1.0, -1.0),
             };
 
@@ -152,10 +167,10 @@ namespace AtomSampleViewer
             bufferData.m_indices =
             {
                 {
-                    //Back
+                    //Top
                     2, 0, 1,
                     0, 2, 3,
-                    //Front
+                    //Bottom
                     4, 6, 5,
                     6, 4, 7,
                     //Left
@@ -164,10 +179,10 @@ namespace AtomSampleViewer
                     //Right
                     14, 12, 13,
                     15, 12, 14,
-                    //Top
+                    //Front
                     16, 18, 17,
                     18, 16, 19,
-                    //Bottom
+                    //Back
                     22, 20, 21,
                     23, 20, 22,
                 }
@@ -395,6 +410,8 @@ namespace AtomSampleViewer
             return;
         }
 
+        AZ::RPI::XRSpaceNotificationBus::Handler::BusDisconnect();
+
         m_inputAssemblyBuffer = nullptr;
         m_bufferPool = nullptr;
         m_pipelineState = nullptr;

+ 21 - 1
Gem/Code/Source/RHI/XRExampleComponent.h

@@ -11,6 +11,7 @@
 #include <AzCore/Component/Component.h>
 
 #include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
+#include <Atom/RPI.Public/XR/XRSpaceNotificationBus.h>
 
 #include <Atom/RHI/FrameScheduler.h>
 #include <Atom/RHI/DrawItem.h>
@@ -28,10 +29,15 @@ namespace AtomSampleViewer
 {
     //! The purpose of this sample is to establish a simple XR sample utilizing a simple VR pipeline
     //! It will render a mesh per controller plus one for the front view. It will prove out all the 
-    //! code related related to openxr device, instance, swapchain, session, input, space.       
+    //! code related related to openxr device, instance, swapchain, session, input, space.
+    //! There will be three instances of this class active.
+    //! 1- The main pipline.
+    //! 2- The Left eye pipeline.
+    //! 3- The Right eye pipeline.       
     class XRExampleComponent final
         : public BasicRHIComponent
         , public AZ::TickBus::Handler
+        , public AZ::RPI::XRSpaceNotificationBus::Handler
     {
     public:
         AZ_COMPONENT(XRExampleComponent, "{A7D9A921-1FF9-4078-92BD-169E258456E7}");
@@ -65,6 +71,20 @@ namespace AtomSampleViewer
         // TickBus::Handler
         void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
 
+        // AZ::RPI::XRSpaceNotificationBus::Handler overrides
+        // This notification arrives before this.OnFramePrepare, additionally it is called
+        // after XR System calls XrSystem::BeginFrame. Why this is important? Because these
+        // transformations are calculated by the XR Device based on a predicted display time
+        // for the current frame. If the shaders update the viewSrg with this freash data
+        // the rendered image appears more stable and less jittery.
+        void OnXRSpaceLocationsChanged(
+            const AZ::Transform& baseSpaceToHeadTm,
+            const AZ::Transform& headToLeftEyeTm,
+            const AZ::Transform& headToRightEyeTm) override;
+
+        // Controller (aka Joysticks) poses/transforms are relative to the head transform.
+        AZ::Transform m_baseSpaceToHeadTm;
+
         //! Create IA data
         void CreateCubeInputAssemblyBuffer();
         //! Create Cube data

+ 2 - 2
project.json

@@ -37,11 +37,11 @@
         "DiffuseProbeGrid",
         "Atom_TestData",
         {
-            "name": "OpenXRVk",
+            "name": "OpenXRVk>=1.0.1",
             "optional": true
         },
         {
-            "name": "XR",
+            "name": "XR>=1.0.1",
             "optional": true
         }
     ]