summaryrefslogtreecommitdiffstats
path: root/dom/vr/XRFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/vr/XRFrame.cpp')
-rw-r--r--dom/vr/XRFrame.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/dom/vr/XRFrame.cpp b/dom/vr/XRFrame.cpp
new file mode 100644
index 0000000000..639bb2b019
--- /dev/null
+++ b/dom/vr/XRFrame.cpp
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/XRFrame.h"
+#include "mozilla/dom/XRRenderState.h"
+#include "mozilla/dom/XRRigidTransform.h"
+#include "mozilla/dom/XRViewerPose.h"
+#include "mozilla/dom/XRView.h"
+#include "mozilla/dom/XRReferenceSpace.h"
+#include "VRDisplayClient.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRFrame, mParent, mSession)
+
+XRFrame::XRFrame(nsISupports* aParent, XRSession* aXRSession)
+ : mParent(aParent),
+ mSession(aXRSession),
+ mActive(false),
+ mAnimationFrame(false) {}
+
+JSObject* XRFrame::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return XRFrame_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+XRSession* XRFrame::Session() { return mSession; }
+
+already_AddRefed<XRViewerPose> XRFrame::GetViewerPose(
+ const XRReferenceSpace& aReferenceSpace, ErrorResult& aRv) {
+ if (!mActive || !mAnimationFrame) {
+ aRv.ThrowInvalidStateError(
+ "GetViewerPose can only be called on an XRFrame during an "
+ "XRSession.requestAnimationFrame callback.");
+ return nullptr;
+ }
+
+ if (aReferenceSpace.GetSession() != mSession) {
+ aRv.ThrowInvalidStateError(
+ "The XRReferenceSpace passed to GetViewerPose must belong to the "
+ "XRSession that GetViewerPose is called on.");
+ return nullptr;
+ }
+
+ if (!mSession->CanReportPoses()) {
+ aRv.ThrowSecurityError(
+ "The visibilityState of the XRSpace's XRSession "
+ "that is passed to GetViewerPose must be 'visible'.");
+ return nullptr;
+ }
+
+ // TODO (Bug 1616393) - Check if poses must be limited:
+ // https://immersive-web.github.io/webxr/#poses-must-be-limited
+
+ bool emulatedPosition = aReferenceSpace.IsPositionEmulated();
+
+ XRRenderState* renderState = mSession->GetActiveRenderState();
+ float depthNear = (float)renderState->DepthNear();
+ float depthFar = (float)renderState->DepthFar();
+
+ RefPtr<XRViewerPose> viewerPose;
+
+ gfx::VRDisplayClient* display = mSession->GetDisplayClient();
+ if (display) {
+ // Have a VRDisplayClient
+ const gfx::VRDisplayInfo& displayInfo =
+ mSession->GetDisplayClient()->GetDisplayInfo();
+ const gfx::VRHMDSensorState& sensorState = display->GetSensorState();
+
+ gfx::PointDouble3D viewerPosition = gfx::PointDouble3D(
+ sensorState.pose.position[0], sensorState.pose.position[1],
+ sensorState.pose.position[2]);
+ gfx::QuaternionDouble viewerOrientation = gfx::QuaternionDouble(
+ sensorState.pose.orientation[0], sensorState.pose.orientation[1],
+ sensorState.pose.orientation[2], sensorState.pose.orientation[3]);
+
+ gfx::Matrix4x4Double headTransform;
+ headTransform.SetRotationFromQuaternion(viewerOrientation);
+ headTransform.PostTranslate(viewerPosition);
+
+ gfx::Matrix4x4Double originTransform;
+ originTransform.SetRotationFromQuaternion(
+ aReferenceSpace.GetEffectiveOriginOrientation().Inverse());
+ originTransform.PreTranslate(-aReferenceSpace.GetEffectiveOriginPosition());
+
+ headTransform *= originTransform;
+
+ viewerPose = mSession->PooledViewerPose(headTransform, emulatedPosition);
+
+ auto updateEye = [&](int32_t viewIndex, gfx::VRDisplayState::Eye eye) {
+ auto offset = displayInfo.GetEyeTranslation(eye);
+ auto eyeFromHead = gfx::Matrix4x4Double::Translation(
+ gfx::PointDouble3D(offset.x, offset.y, offset.z));
+ auto eyeTransform = eyeFromHead * headTransform;
+ gfx::PointDouble3D eyePosition;
+ gfx::QuaternionDouble eyeRotation;
+ gfx::PointDouble3D eyeScale;
+ eyeTransform.Decompose(eyePosition, eyeRotation, eyeScale);
+
+ const gfx::VRFieldOfView fov = displayInfo.mDisplayState.eyeFOV[eye];
+ gfx::Matrix4x4 projection =
+ fov.ConstructProjectionMatrix(depthNear, depthFar, true);
+ viewerPose->GetEye(viewIndex)->Update(eyePosition, eyeRotation,
+ projection);
+ };
+
+ updateEye(0, gfx::VRDisplayState::Eye_Left);
+ updateEye(1, gfx::VRDisplayState::Eye_Right);
+ } else {
+ auto inlineVerticalFov = renderState->GetInlineVerticalFieldOfView();
+ const double fov =
+ inlineVerticalFov.IsNull() ? M_PI * 0.5f : inlineVerticalFov.Value();
+ HTMLCanvasElement* canvas = renderState->GetOutputCanvas();
+ float aspect = 1.0f;
+ if (canvas) {
+ aspect = (float)canvas->Width() / (float)canvas->Height();
+ }
+ gfx::Matrix4x4 projection =
+ ConstructInlineProjection((float)fov, aspect, depthNear, depthFar);
+
+ viewerPose =
+ mSession->PooledViewerPose(gfx::Matrix4x4Double(), emulatedPosition);
+ viewerPose->GetEye(0)->Update(gfx::PointDouble3D(), gfx::QuaternionDouble(),
+ projection);
+ }
+
+ return viewerPose.forget();
+}
+
+already_AddRefed<XRPose> XRFrame::GetPose(const XRSpace& aSpace,
+ const XRSpace& aBaseSpace,
+ ErrorResult& aRv) {
+ if (!mActive) {
+ aRv.ThrowInvalidStateError(
+ "GetPose can not be called on an XRFrame that is not active.");
+ return nullptr;
+ }
+
+ if (aSpace.GetSession() != mSession || aBaseSpace.GetSession() != mSession) {
+ aRv.ThrowInvalidStateError(
+ "The XRSpace passed to GetPose must belong to the "
+ "XRSession that GetPose is called on.");
+ return nullptr;
+ }
+
+ if (!mSession->CanReportPoses()) {
+ aRv.ThrowSecurityError(
+ "The visibilityState of the XRSpace's XRSession "
+ "that is passed to GetPose must be 'visible'.");
+ return nullptr;
+ }
+
+ // TODO (Bug 1616393) - Check if poses must be limited:
+ // https://immersive-web.github.io/webxr/#poses-must-be-limited
+
+ const bool emulatedPosition = aSpace.IsPositionEmulated();
+ gfx::Matrix4x4Double base;
+ base.SetRotationFromQuaternion(
+ aBaseSpace.GetEffectiveOriginOrientation().Inverse());
+ base.PreTranslate(-aBaseSpace.GetEffectiveOriginPosition());
+
+ gfx::Matrix4x4Double matrix = aSpace.GetEffectiveOriginTransform() * base;
+
+ RefPtr<XRRigidTransform> transform = new XRRigidTransform(mParent, matrix);
+ RefPtr<XRPose> pose = new XRPose(mParent, transform, emulatedPosition);
+
+ return pose.forget();
+}
+
+void XRFrame::StartAnimationFrame() {
+ mActive = true;
+ mAnimationFrame = true;
+}
+
+void XRFrame::EndAnimationFrame() { mActive = false; }
+
+void XRFrame::StartInputSourceEvent() { mActive = true; }
+
+void XRFrame::EndInputSourceEvent() { mActive = false; }
+
+gfx::Matrix4x4 XRFrame::ConstructInlineProjection(float aFov, float aAspect,
+ float aNear, float aFar) {
+ gfx::Matrix4x4 m;
+ const float depth = aFar - aNear;
+ const float invDepth = 1 / depth;
+ if (aFov == 0) {
+ aFov = 0.5f * M_PI;
+ }
+
+ m._22 = 1.0f / tan(0.5f * aFov);
+ m._11 = -m._22 / aAspect;
+ m._33 = depth * invDepth;
+ m._43 = (-aFar * aNear) * invDepth;
+ m._34 = 1.0f;
+
+ return m;
+}
+
+} // namespace mozilla::dom