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/. */
7 #include "mozilla/dom/XRFrame.h"
8 #include "mozilla/dom/XRRenderState.h"
9 #include "mozilla/dom/XRRigidTransform.h"
10 #include "mozilla/dom/XRViewerPose.h"
11 #include "mozilla/dom/XRView.h"
12 #include "mozilla/dom/XRReferenceSpace.h"
13 #include "VRDisplayClient.h"
15 namespace mozilla::dom
{
17 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRFrame
, mParent
, mSession
)
19 XRFrame::XRFrame(nsISupports
* aParent
, XRSession
* aXRSession
)
23 mAnimationFrame(false) {}
25 JSObject
* XRFrame::WrapObject(JSContext
* aCx
,
26 JS::Handle
<JSObject
*> aGivenProto
) {
27 return XRFrame_Binding::Wrap(aCx
, this, aGivenProto
);
30 XRSession
* XRFrame::Session() { return mSession
; }
32 already_AddRefed
<XRViewerPose
> XRFrame::GetViewerPose(
33 const XRReferenceSpace
& aReferenceSpace
, ErrorResult
& aRv
) {
34 if (!mActive
|| !mAnimationFrame
) {
35 aRv
.ThrowInvalidStateError(
36 "GetViewerPose can only be called on an XRFrame during an "
37 "XRSession.requestAnimationFrame callback.");
41 if (aReferenceSpace
.GetSession() != mSession
) {
42 aRv
.ThrowInvalidStateError(
43 "The XRReferenceSpace passed to GetViewerPose must belong to the "
44 "XRSession that GetViewerPose is called on.");
48 if (!mSession
->CanReportPoses()) {
49 aRv
.ThrowSecurityError(
50 "The visibilityState of the XRSpace's XRSession "
51 "that is passed to GetViewerPose must be 'visible'.");
55 // TODO (Bug 1616393) - Check if poses must be limited:
56 // https://immersive-web.github.io/webxr/#poses-must-be-limited
58 bool emulatedPosition
= aReferenceSpace
.IsPositionEmulated();
60 XRRenderState
* renderState
= mSession
->GetActiveRenderState();
61 float depthNear
= (float)renderState
->DepthNear();
62 float depthFar
= (float)renderState
->DepthFar();
64 RefPtr
<XRViewerPose
> viewerPose
;
66 gfx::VRDisplayClient
* display
= mSession
->GetDisplayClient();
68 // Have a VRDisplayClient
69 const gfx::VRDisplayInfo
& displayInfo
=
70 mSession
->GetDisplayClient()->GetDisplayInfo();
71 const gfx::VRHMDSensorState
& sensorState
= display
->GetSensorState();
73 gfx::PointDouble3D viewerPosition
= gfx::PointDouble3D(
74 sensorState
.pose
.position
[0], sensorState
.pose
.position
[1],
75 sensorState
.pose
.position
[2]);
76 gfx::QuaternionDouble viewerOrientation
= gfx::QuaternionDouble(
77 sensorState
.pose
.orientation
[0], sensorState
.pose
.orientation
[1],
78 sensorState
.pose
.orientation
[2], sensorState
.pose
.orientation
[3]);
80 gfx::Matrix4x4Double headTransform
;
81 headTransform
.SetRotationFromQuaternion(viewerOrientation
);
82 headTransform
.PostTranslate(viewerPosition
);
84 gfx::Matrix4x4Double originTransform
;
85 originTransform
.SetRotationFromQuaternion(
86 aReferenceSpace
.GetEffectiveOriginOrientation().Inverse());
87 originTransform
.PreTranslate(-aReferenceSpace
.GetEffectiveOriginPosition());
89 headTransform
*= originTransform
;
91 viewerPose
= mSession
->PooledViewerPose(headTransform
, emulatedPosition
);
93 auto updateEye
= [&](int32_t viewIndex
, gfx::VRDisplayState::Eye eye
) {
94 auto offset
= displayInfo
.GetEyeTranslation(eye
);
95 auto eyeFromHead
= gfx::Matrix4x4Double::Translation(
96 gfx::PointDouble3D(offset
.x
, offset
.y
, offset
.z
));
97 auto eyeTransform
= eyeFromHead
* headTransform
;
98 gfx::PointDouble3D eyePosition
;
99 gfx::QuaternionDouble eyeRotation
;
100 gfx::PointDouble3D eyeScale
;
101 eyeTransform
.Decompose(eyePosition
, eyeRotation
, eyeScale
);
103 const gfx::VRFieldOfView fov
= displayInfo
.mDisplayState
.eyeFOV
[eye
];
104 gfx::Matrix4x4 projection
=
105 fov
.ConstructProjectionMatrix(depthNear
, depthFar
, true);
106 viewerPose
->GetEye(viewIndex
)->Update(eyePosition
, eyeRotation
,
110 updateEye(0, gfx::VRDisplayState::Eye_Left
);
111 updateEye(1, gfx::VRDisplayState::Eye_Right
);
113 auto inlineVerticalFov
= renderState
->GetInlineVerticalFieldOfView();
115 inlineVerticalFov
.IsNull() ? M_PI
* 0.5f
: inlineVerticalFov
.Value();
116 HTMLCanvasElement
* canvas
= renderState
->GetOutputCanvas();
119 aspect
= (float)canvas
->Width() / (float)canvas
->Height();
121 gfx::Matrix4x4 projection
=
122 ConstructInlineProjection((float)fov
, aspect
, depthNear
, depthFar
);
125 mSession
->PooledViewerPose(gfx::Matrix4x4Double(), emulatedPosition
);
126 viewerPose
->GetEye(0)->Update(gfx::PointDouble3D(), gfx::QuaternionDouble(),
130 return viewerPose
.forget();
133 already_AddRefed
<XRPose
> XRFrame::GetPose(const XRSpace
& aSpace
,
134 const XRSpace
& aBaseSpace
,
137 aRv
.ThrowInvalidStateError(
138 "GetPose can not be called on an XRFrame that is not active.");
142 if (aSpace
.GetSession() != mSession
|| aBaseSpace
.GetSession() != mSession
) {
143 aRv
.ThrowInvalidStateError(
144 "The XRSpace passed to GetPose must belong to the "
145 "XRSession that GetPose is called on.");
149 if (!mSession
->CanReportPoses()) {
150 aRv
.ThrowSecurityError(
151 "The visibilityState of the XRSpace's XRSession "
152 "that is passed to GetPose must be 'visible'.");
156 // TODO (Bug 1616393) - Check if poses must be limited:
157 // https://immersive-web.github.io/webxr/#poses-must-be-limited
159 const bool emulatedPosition
= aSpace
.IsPositionEmulated();
160 gfx::Matrix4x4Double base
;
161 base
.SetRotationFromQuaternion(
162 aBaseSpace
.GetEffectiveOriginOrientation().Inverse());
163 base
.PreTranslate(-aBaseSpace
.GetEffectiveOriginPosition());
165 gfx::Matrix4x4Double matrix
= aSpace
.GetEffectiveOriginTransform() * base
;
167 RefPtr
<XRRigidTransform
> transform
= new XRRigidTransform(mParent
, matrix
);
168 RefPtr
<XRPose
> pose
= new XRPose(mParent
, transform
, emulatedPosition
);
170 return pose
.forget();
173 void XRFrame::StartAnimationFrame() {
175 mAnimationFrame
= true;
178 void XRFrame::EndAnimationFrame() { mActive
= false; }
180 void XRFrame::StartInputSourceEvent() { mActive
= true; }
182 void XRFrame::EndInputSourceEvent() { mActive
= false; }
184 gfx::Matrix4x4
XRFrame::ConstructInlineProjection(float aFov
, float aAspect
,
185 float aNear
, float aFar
) {
187 const float depth
= aFar
- aNear
;
188 const float invDepth
= 1 / depth
;
193 m
._22
= 1.0f
/ tan(0.5f
* aFov
);
194 m
._11
= -m
._22
/ aAspect
;
195 m
._33
= depth
* invDepth
;
196 m
._43
= (-aFar
* aNear
) * invDepth
;
202 } // namespace mozilla::dom