XRFrame.cpp (7237B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/XRFrame.h" 8 9 #include "VRDisplayClient.h" 10 #include "mozilla/dom/XRReferenceSpace.h" 11 #include "mozilla/dom/XRRenderState.h" 12 #include "mozilla/dom/XRRigidTransform.h" 13 #include "mozilla/dom/XRView.h" 14 #include "mozilla/dom/XRViewerPose.h" 15 16 namespace mozilla::dom { 17 18 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRFrame, mParent, mSession) 19 20 XRFrame::XRFrame(nsISupports* aParent, XRSession* aXRSession) 21 : mParent(aParent), 22 mSession(aXRSession), 23 mActive(false), 24 mAnimationFrame(false) {} 25 26 JSObject* XRFrame::WrapObject(JSContext* aCx, 27 JS::Handle<JSObject*> aGivenProto) { 28 return XRFrame_Binding::Wrap(aCx, this, aGivenProto); 29 } 30 31 XRSession* XRFrame::Session() { return mSession; } 32 33 already_AddRefed<XRViewerPose> XRFrame::GetViewerPose( 34 const XRReferenceSpace& aReferenceSpace, ErrorResult& aRv) { 35 if (!mActive || !mAnimationFrame) { 36 aRv.ThrowInvalidStateError( 37 "GetViewerPose can only be called on an XRFrame during an " 38 "XRSession.requestAnimationFrame callback."); 39 return nullptr; 40 } 41 42 if (aReferenceSpace.GetSession() != mSession) { 43 aRv.ThrowInvalidStateError( 44 "The XRReferenceSpace passed to GetViewerPose must belong to the " 45 "XRSession that GetViewerPose is called on."); 46 return nullptr; 47 } 48 49 if (!mSession->CanReportPoses()) { 50 aRv.ThrowSecurityError( 51 "The visibilityState of the XRSpace's XRSession " 52 "that is passed to GetViewerPose must be 'visible'."); 53 return nullptr; 54 } 55 56 // TODO (Bug 1616393) - Check if poses must be limited: 57 // https://immersive-web.github.io/webxr/#poses-must-be-limited 58 59 bool emulatedPosition = aReferenceSpace.IsPositionEmulated(); 60 61 XRRenderState* renderState = mSession->GetActiveRenderState(); 62 float depthNear = (float)renderState->DepthNear(); 63 float depthFar = (float)renderState->DepthFar(); 64 65 RefPtr<XRViewerPose> viewerPose; 66 67 gfx::VRDisplayClient* display = mSession->GetDisplayClient(); 68 if (display) { 69 // Have a VRDisplayClient 70 const gfx::VRDisplayInfo& displayInfo = 71 mSession->GetDisplayClient()->GetDisplayInfo(); 72 const gfx::VRHMDSensorState& sensorState = display->GetSensorState(); 73 74 gfx::PointDouble3D viewerPosition = gfx::PointDouble3D( 75 sensorState.pose.position[0], sensorState.pose.position[1], 76 sensorState.pose.position[2]); 77 gfx::QuaternionDouble viewerOrientation = gfx::QuaternionDouble( 78 sensorState.pose.orientation[0], sensorState.pose.orientation[1], 79 sensorState.pose.orientation[2], sensorState.pose.orientation[3]); 80 81 gfx::Matrix4x4Double headTransform; 82 headTransform.SetRotationFromQuaternion(viewerOrientation); 83 headTransform.PostTranslate(viewerPosition); 84 85 gfx::Matrix4x4Double originTransform; 86 originTransform.SetRotationFromQuaternion( 87 aReferenceSpace.GetEffectiveOriginOrientation().Inverse()); 88 originTransform.PreTranslate(-aReferenceSpace.GetEffectiveOriginPosition()); 89 90 headTransform *= originTransform; 91 92 viewerPose = mSession->PooledViewerPose(headTransform, emulatedPosition); 93 94 auto updateEye = [&](int32_t viewIndex, gfx::VRDisplayState::Eye eye) { 95 auto offset = displayInfo.GetEyeTranslation(eye); 96 auto eyeFromHead = gfx::Matrix4x4Double::Translation( 97 gfx::PointDouble3D(offset.x, offset.y, offset.z)); 98 auto eyeTransform = eyeFromHead * headTransform; 99 gfx::PointDouble3D eyePosition; 100 gfx::QuaternionDouble eyeRotation; 101 gfx::PointDouble3D eyeScale; 102 eyeTransform.Decompose(eyePosition, eyeRotation, eyeScale); 103 104 const gfx::VRFieldOfView fov = displayInfo.mDisplayState.eyeFOV[eye]; 105 gfx::Matrix4x4 projection = 106 fov.ConstructProjectionMatrix(depthNear, depthFar, true); 107 viewerPose->GetEye(viewIndex)->Update(eyePosition, eyeRotation, 108 projection); 109 }; 110 111 updateEye(0, gfx::VRDisplayState::Eye_Left); 112 updateEye(1, gfx::VRDisplayState::Eye_Right); 113 } else { 114 auto inlineVerticalFov = renderState->GetInlineVerticalFieldOfView(); 115 const double fov = 116 inlineVerticalFov.IsNull() ? M_PI * 0.5f : inlineVerticalFov.Value(); 117 HTMLCanvasElement* canvas = renderState->GetOutputCanvas(); 118 float aspect = 1.0f; 119 if (canvas) { 120 aspect = (float)canvas->Width() / (float)canvas->Height(); 121 } 122 gfx::Matrix4x4 projection = 123 ConstructInlineProjection((float)fov, aspect, depthNear, depthFar); 124 125 viewerPose = 126 mSession->PooledViewerPose(gfx::Matrix4x4Double(), emulatedPosition); 127 viewerPose->GetEye(0)->Update(gfx::PointDouble3D(), gfx::QuaternionDouble(), 128 projection); 129 } 130 131 return viewerPose.forget(); 132 } 133 134 already_AddRefed<XRPose> XRFrame::GetPose(const XRSpace& aSpace, 135 const XRSpace& aBaseSpace, 136 ErrorResult& aRv) { 137 if (!mActive) { 138 aRv.ThrowInvalidStateError( 139 "GetPose can not be called on an XRFrame that is not active."); 140 return nullptr; 141 } 142 143 if (aSpace.GetSession() != mSession || aBaseSpace.GetSession() != mSession) { 144 aRv.ThrowInvalidStateError( 145 "The XRSpace passed to GetPose must belong to the " 146 "XRSession that GetPose is called on."); 147 return nullptr; 148 } 149 150 if (!mSession->CanReportPoses()) { 151 aRv.ThrowSecurityError( 152 "The visibilityState of the XRSpace's XRSession " 153 "that is passed to GetPose must be 'visible'."); 154 return nullptr; 155 } 156 157 // TODO (Bug 1616393) - Check if poses must be limited: 158 // https://immersive-web.github.io/webxr/#poses-must-be-limited 159 160 const bool emulatedPosition = aSpace.IsPositionEmulated(); 161 gfx::Matrix4x4Double base; 162 base.SetRotationFromQuaternion( 163 aBaseSpace.GetEffectiveOriginOrientation().Inverse()); 164 base.PreTranslate(-aBaseSpace.GetEffectiveOriginPosition()); 165 166 gfx::Matrix4x4Double matrix = aSpace.GetEffectiveOriginTransform() * base; 167 168 RefPtr<XRRigidTransform> transform = new XRRigidTransform(mParent, matrix); 169 RefPtr<XRPose> pose = new XRPose(mParent, transform, emulatedPosition); 170 171 return pose.forget(); 172 } 173 174 void XRFrame::StartAnimationFrame() { 175 mActive = true; 176 mAnimationFrame = true; 177 } 178 179 void XRFrame::EndAnimationFrame() { mActive = false; } 180 181 void XRFrame::StartInputSourceEvent() { mActive = true; } 182 183 void XRFrame::EndInputSourceEvent() { mActive = false; } 184 185 gfx::Matrix4x4 XRFrame::ConstructInlineProjection(float aFov, float aAspect, 186 float aNear, float aFar) { 187 gfx::Matrix4x4 m; 188 const float depth = aFar - aNear; 189 const float invDepth = 1 / depth; 190 if (aFov == 0) { 191 aFov = 0.5f * M_PI; 192 } 193 194 m._22 = 1.0f / tan(0.5f * aFov); 195 m._11 = -m._22 / aAspect; 196 m._33 = depth * invDepth; 197 m._43 = (-aFar * aNear) * invDepth; 198 m._34 = 1.0f; 199 200 return m; 201 } 202 203 } // namespace mozilla::dom