diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /dom/vr/XRFrame.cpp | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | dom/vr/XRFrame.cpp | 202 |
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 |