diff options
Diffstat (limited to 'dom/vr/VRDisplay.cpp')
-rw-r--r-- | dom/vr/VRDisplay.cpp | 802 |
1 files changed, 802 insertions, 0 deletions
diff --git a/dom/vr/VRDisplay.cpp b/dom/vr/VRDisplay.cpp new file mode 100644 index 0000000000..80922422f9 --- /dev/null +++ b/dom/vr/VRDisplay.cpp @@ -0,0 +1,802 @@ +/* -*- 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 "nsWrapperCache.h" + +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ElementBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/VRDisplay.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/dom/VRDisplayBinding.h" +#include "Navigator.h" +#include "gfxVR.h" +#include "VRDisplayClient.h" +#include "VRManagerChild.h" +#include "VRDisplayPresentation.h" +#include "nsIObserverService.h" +#include "nsIFrame.h" +#include "nsISupportsPrimitives.h" + +using namespace mozilla::gfx; + +namespace mozilla { +namespace dom { + +VRFieldOfView::VRFieldOfView(nsISupports* aParent, + double aUpDegrees, double aRightDegrees, + double aDownDegrees, double aLeftDegrees) + : mParent(aParent) + , mUpDegrees(aUpDegrees) + , mRightDegrees(aRightDegrees) + , mDownDegrees(aDownDegrees) + , mLeftDegrees(aLeftDegrees) +{ +} + +VRFieldOfView::VRFieldOfView(nsISupports* aParent, const gfx::VRFieldOfView& aSrc) + : mParent(aParent) + , mUpDegrees(aSrc.upDegrees) + , mRightDegrees(aSrc.rightDegrees) + , mDownDegrees(aSrc.downDegrees) + , mLeftDegrees(aSrc.leftDegrees) +{ +} + +bool +VRDisplayCapabilities::HasPosition() const +{ + return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Position); +} + +bool +VRDisplayCapabilities::HasOrientation() const +{ + return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Orientation); +} + +bool +VRDisplayCapabilities::HasExternalDisplay() const +{ + return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_External); +} + +bool +VRDisplayCapabilities::CanPresent() const +{ + return bool(mFlags & gfx::VRDisplayCapabilityFlags::Cap_Present); +} + +uint32_t +VRDisplayCapabilities::MaxLayers() const +{ + return CanPresent() ? 1 : 0; +} + +/*static*/ bool +VRDisplay::RefreshVRDisplays(uint64_t aWindowId) +{ + gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); + return vm && vm->RefreshVRDisplaysWithCallback(aWindowId); +} + +/*static*/ void +VRDisplay::UpdateVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays, nsPIDOMWindowInner* aWindow) +{ + nsTArray<RefPtr<VRDisplay>> displays; + + gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); + nsTArray<RefPtr<gfx::VRDisplayClient>> updatedDisplays; + if (vm && vm->GetVRDisplays(updatedDisplays)) { + for (size_t i = 0; i < updatedDisplays.Length(); i++) { + RefPtr<gfx::VRDisplayClient> display = updatedDisplays[i]; + bool isNewDisplay = true; + for (size_t j = 0; j < aDisplays.Length(); j++) { + if (aDisplays[j]->GetClient()->GetDisplayInfo() == display->GetDisplayInfo()) { + displays.AppendElement(aDisplays[j]); + isNewDisplay = false; + } + } + + if (isNewDisplay) { + displays.AppendElement(new VRDisplay(aWindow, display)); + } + } + } + + aDisplays = displays; +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRFieldOfView, mParent) +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFieldOfView, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFieldOfView, Release) + + +JSObject* +VRFieldOfView::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) +{ + return VRFieldOfViewBinding::Wrap(aCx, this, aGivenProto); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(VREyeParameters) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VREyeParameters) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mFOV) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + tmp->mOffset = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VREyeParameters) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mFOV) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VREyeParameters) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOffset) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VREyeParameters, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VREyeParameters, Release) + +VREyeParameters::VREyeParameters(nsISupports* aParent, + const gfx::Point3D& aEyeTranslation, + const gfx::VRFieldOfView& aFOV, + const gfx::IntSize& aRenderSize) + : mParent(aParent) + , mEyeTranslation(aEyeTranslation) + , mRenderSize(aRenderSize) +{ + mFOV = new VRFieldOfView(aParent, aFOV); + mozilla::HoldJSObjects(this); +} + +VREyeParameters::~VREyeParameters() +{ + mozilla::DropJSObjects(this); +} + +VRFieldOfView* +VREyeParameters::FieldOfView() +{ + return mFOV; +} + +void +VREyeParameters::GetOffset(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) +{ + if (!mOffset) { + // Lazily create the Float32Array + mOffset = dom::Float32Array::Create(aCx, this, 3, mEyeTranslation.components); + if (!mOffset) { + aRv.NoteJSContextException(aCx); + return; + } + } + aRetval.set(mOffset); +} + +JSObject* +VREyeParameters::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return VREyeParametersBinding::Wrap(aCx, this, aGivenProto); +} + +VRStageParameters::VRStageParameters(nsISupports* aParent, + const gfx::Matrix4x4& aSittingToStandingTransform, + const gfx::Size& aSize) + : mParent(aParent) + , mSittingToStandingTransform(aSittingToStandingTransform) + , mSittingToStandingTransformArray(nullptr) + , mSize(aSize) +{ + mozilla::HoldJSObjects(this); +} + +VRStageParameters::~VRStageParameters() +{ + mozilla::DropJSObjects(this); +} + +JSObject* +VRStageParameters::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return VRStageParametersBinding::Wrap(aCx, this, aGivenProto); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(VRStageParameters) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRStageParameters) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + tmp->mSittingToStandingTransformArray = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRStageParameters) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRStageParameters) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mSittingToStandingTransformArray) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRStageParameters, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRStageParameters, Release) + +void +VRStageParameters::GetSittingToStandingTransform(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + if (!mSittingToStandingTransformArray) { + // Lazily create the Float32Array + mSittingToStandingTransformArray = dom::Float32Array::Create(aCx, this, 16, + mSittingToStandingTransform.components); + if (!mSittingToStandingTransformArray) { + aRv.NoteJSContextException(aCx); + return; + } + } + aRetval.set(mSittingToStandingTransformArray); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(VRDisplayCapabilities, mParent) +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRDisplayCapabilities, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRDisplayCapabilities, Release) + +JSObject* +VRDisplayCapabilities::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return VRDisplayCapabilitiesBinding::Wrap(aCx, this, aGivenProto); +} + +VRPose::VRPose(nsISupports* aParent, const gfx::VRHMDSensorState& aState) + : Pose(aParent) + , mVRState(aState) +{ + mFrameId = aState.inputFrameID; + mozilla::HoldJSObjects(this); +} + +VRPose::VRPose(nsISupports* aParent) + : Pose(aParent) +{ + mFrameId = 0; + mVRState.Clear(); + mozilla::HoldJSObjects(this); +} + +VRPose::~VRPose() +{ + mozilla::DropJSObjects(this); +} + +void +VRPose::GetPosition(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + SetFloat32Array(aCx, aRetval, mPosition, mVRState.position, 3, + !mPosition && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position), + aRv); +} + +void +VRPose::GetLinearVelocity(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + SetFloat32Array(aCx, aRetval, mLinearVelocity, mVRState.linearVelocity, 3, + !mLinearVelocity && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position), + aRv); +} + +void +VRPose::GetLinearAcceleration(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + SetFloat32Array(aCx, aRetval, mLinearAcceleration, mVRState.linearAcceleration, 3, + !mLinearAcceleration && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_LinearAcceleration), + aRv); + +} + +void +VRPose::GetOrientation(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + SetFloat32Array(aCx, aRetval, mOrientation, mVRState.orientation, 4, + !mOrientation && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation), + aRv); +} + +void +VRPose::GetAngularVelocity(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + SetFloat32Array(aCx, aRetval, mAngularVelocity, mVRState.angularVelocity, 3, + !mAngularVelocity && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation), + aRv); +} + +void +VRPose::GetAngularAcceleration(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + SetFloat32Array(aCx, aRetval, mAngularAcceleration, mVRState.angularAcceleration, 3, + !mAngularAcceleration && bool(mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_AngularAcceleration), + aRv); +} + +JSObject* +VRPose::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return VRPoseBinding::Wrap(aCx, this, aGivenProto); +} + +/* virtual */ JSObject* +VRDisplay::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return VRDisplayBinding::Wrap(aCx, this, aGivenProto); +} + +VRDisplay::VRDisplay(nsPIDOMWindowInner* aWindow, gfx::VRDisplayClient* aClient) + : DOMEventTargetHelper(aWindow) + , mClient(aClient) + , mDepthNear(0.01f) // Default value from WebVR Spec + , mDepthFar(10000.0f) // Default value from WebVR Spec +{ + const gfx::VRDisplayInfo& info = aClient->GetDisplayInfo(); + mDisplayId = info.GetDisplayID(); + mDisplayName = NS_ConvertASCIItoUTF16(info.GetDisplayName()); + mCapabilities = new VRDisplayCapabilities(aWindow, info.GetCapabilities()); + if (info.GetCapabilities() & gfx::VRDisplayCapabilityFlags::Cap_StageParameters) { + mStageParameters = new VRStageParameters(aWindow, + info.GetSittingToStandingTransform(), + info.GetStageSize()); + } + mozilla::HoldJSObjects(this); +} + +VRDisplay::~VRDisplay() +{ + ExitPresentInternal(); + mozilla::DropJSObjects(this); +} + +void +VRDisplay::LastRelease() +{ + // We don't want to wait for the CC to free up the presentation + // for use in other documents, so we do this in LastRelease(). + ExitPresentInternal(); +} + +already_AddRefed<VREyeParameters> +VRDisplay::GetEyeParameters(VREye aEye) +{ + gfx::VRDisplayInfo::Eye eye = aEye == VREye::Left ? gfx::VRDisplayInfo::Eye_Left : gfx::VRDisplayInfo::Eye_Right; + RefPtr<VREyeParameters> params = + new VREyeParameters(GetParentObject(), + mClient->GetDisplayInfo().GetEyeTranslation(eye), + mClient->GetDisplayInfo().GetEyeFOV(eye), + mClient->GetDisplayInfo().SuggestedEyeResolution()); + return params.forget(); +} + +VRDisplayCapabilities* +VRDisplay::Capabilities() +{ + return mCapabilities; +} + +VRStageParameters* +VRDisplay::GetStageParameters() +{ + return mStageParameters; +} + +void +VRDisplay::UpdateFrameInfo() +{ + /** + * The WebVR 1.1 spec Requires that VRDisplay.getPose and VRDisplay.getFrameData + * must return the same values until the next VRDisplay.submitFrame. + * + * mFrameInfo is marked dirty at the end of the frame or start of a new + * composition and lazily created here in order to receive mid-frame + * pose-prediction updates while still ensuring conformance to the WebVR spec + * requirements. + * + * If we are not presenting WebVR content, the frame will never end and we should + * return the latest frame data always. + */ + if (mFrameInfo.IsDirty() || !mPresentation) { + gfx::VRHMDSensorState state = mClient->GetSensorState(); + const gfx::VRDisplayInfo& info = mClient->GetDisplayInfo(); + mFrameInfo.Update(info, state, mDepthNear, mDepthFar); + } +} + +bool +VRDisplay::GetFrameData(VRFrameData& aFrameData) +{ + UpdateFrameInfo(); + aFrameData.Update(mFrameInfo); + return true; +} + +already_AddRefed<VRPose> +VRDisplay::GetPose() +{ + UpdateFrameInfo(); + RefPtr<VRPose> obj = new VRPose(GetParentObject(), mFrameInfo.mVRState); + + return obj.forget(); +} + +void +VRDisplay::ResetPose() +{ + mClient->ZeroSensor(); +} + +already_AddRefed<Promise> +VRDisplay::RequestPresent(const nsTArray<VRLayer>& aLayers, ErrorResult& aRv) +{ + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<Promise> promise = Promise::Create(global, aRv); + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); + + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + NS_ENSURE_TRUE(obs, nullptr); + + if (mClient->GetIsPresenting()) { + // Only one presentation allowed per VRDisplay + // on a first-come-first-serve basis. + promise->MaybeRejectWithUndefined(); + } else { + mPresentation = mClient->BeginPresentation(aLayers); + mFrameInfo.Clear(); + + nsresult rv = obs->AddObserver(this, "inner-window-destroyed", false); + if (NS_WARN_IF(NS_FAILED(rv))) { + mPresentation = nullptr; + promise->MaybeRejectWithUndefined(); + } else { + promise->MaybeResolve(JS::UndefinedHandleValue); + } + } + return promise.forget(); +} + +NS_IMETHODIMP +VRDisplay::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (strcmp(aTopic, "inner-window-destroyed") == 0) { + nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject); + NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); + + uint64_t innerID; + nsresult rv = wrapper->GetData(&innerID); + NS_ENSURE_SUCCESS(rv, rv); + + if (!GetOwner() || GetOwner()->WindowID() == innerID) { + ExitPresentInternal(); + } + + return NS_OK; + } + + // This should not happen. + return NS_ERROR_FAILURE; +} + +already_AddRefed<Promise> +VRDisplay::ExitPresent(ErrorResult& aRv) +{ + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); + if (!global) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + + RefPtr<Promise> promise = Promise::Create(global, aRv); + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); + + if (!IsPresenting()) { + // We can not exit a presentation outside of the context that + // started the presentation. + promise->MaybeRejectWithUndefined(); + } else { + promise->MaybeResolve(JS::UndefinedHandleValue); + ExitPresentInternal(); + } + + return promise.forget(); +} + +void +VRDisplay::ExitPresentInternal() +{ + mPresentation = nullptr; +} + +void +VRDisplay::GetLayers(nsTArray<VRLayer>& result) +{ + if (mPresentation) { + mPresentation->GetDOMLayers(result); + } else { + result = nsTArray<VRLayer>(); + } +} + +void +VRDisplay::SubmitFrame() +{ + if (mPresentation) { + mPresentation->SubmitFrame(); + } + mFrameInfo.Clear(); +} + +int32_t +VRDisplay::RequestAnimationFrame(FrameRequestCallback& aCallback, +ErrorResult& aError) +{ + gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); + + int32_t handle; + aError = vm->ScheduleFrameRequestCallback(aCallback, &handle); + return handle; +} + +void +VRDisplay::CancelAnimationFrame(int32_t aHandle, ErrorResult& aError) +{ + gfx::VRManagerChild* vm = gfx::VRManagerChild::Get(); + vm->CancelFrameRequestCallback(aHandle); +} + + +bool +VRDisplay::IsPresenting() const +{ + // IsPresenting returns true only if this Javascript context is presenting + // and will return false if another context is presenting. + return mPresentation != nullptr; +} + +bool +VRDisplay::IsConnected() const +{ + return mClient->GetIsConnected(); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(VRDisplay, DOMEventTargetHelper, mCapabilities, mStageParameters) + +NS_IMPL_ADDREF_INHERITED(VRDisplay, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(VRDisplay, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(VRDisplay) +NS_INTERFACE_MAP_ENTRY(nsIObserver) +NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, DOMEventTargetHelper) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_CYCLE_COLLECTION_CLASS(VRFrameData) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(VRFrameData) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent, mPose) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + tmp->mLeftProjectionMatrix = nullptr; + tmp->mLeftViewMatrix = nullptr; + tmp->mRightProjectionMatrix = nullptr; + tmp->mRightViewMatrix = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(VRFrameData) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent, mPose) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(VRFrameData) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftProjectionMatrix) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLeftViewMatrix) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightProjectionMatrix) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRightViewMatrix) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(VRFrameData, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(VRFrameData, Release) + +VRFrameData::VRFrameData(nsISupports* aParent) + : mParent(aParent) + , mLeftProjectionMatrix(nullptr) + , mLeftViewMatrix(nullptr) + , mRightProjectionMatrix(nullptr) + , mRightViewMatrix(nullptr) +{ + mozilla::HoldJSObjects(this); + mPose = new VRPose(aParent); +} + +VRFrameData::~VRFrameData() +{ + mozilla::DropJSObjects(this); +} + +/* static */ already_AddRefed<VRFrameData> +VRFrameData::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + RefPtr<VRFrameData> obj = new VRFrameData(aGlobal.GetAsSupports()); + return obj.forget(); +} + +JSObject* +VRFrameData::WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) +{ + return VRFrameDataBinding::Wrap(aCx, this, aGivenProto); +} + +VRPose* +VRFrameData::Pose() +{ + return mPose; +} + +void +VRFrameData::LazyCreateMatrix(JS::Heap<JSObject*>& aArray, gfx::Matrix4x4& aMat, JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, ErrorResult& aRv) +{ + if (!aArray) { + // Lazily create the Float32Array + aArray = dom::Float32Array::Create(aCx, this, 16, aMat.components); + if (!aArray) { + aRv.NoteJSContextException(aCx); + return; + } + } + if (aArray) { + JS::ExposeObjectToActiveJS(aArray); + } + aRetval.set(aArray); +} + +double +VRFrameData::Timestamp() const +{ + // Converting from seconds to milliseconds + return mFrameInfo.mVRState.timestamp * 1000.0f; +} + +void +VRFrameData::GetLeftProjectionMatrix(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + LazyCreateMatrix(mLeftProjectionMatrix, mFrameInfo.mLeftProjection, aCx, + aRetval, aRv); +} + +void +VRFrameData::GetLeftViewMatrix(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + LazyCreateMatrix(mLeftViewMatrix, mFrameInfo.mLeftView, aCx, aRetval, aRv); +} + +void +VRFrameData::GetRightProjectionMatrix(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + LazyCreateMatrix(mRightProjectionMatrix, mFrameInfo.mRightProjection, aCx, + aRetval, aRv); +} + +void +VRFrameData::GetRightViewMatrix(JSContext* aCx, + JS::MutableHandle<JSObject*> aRetval, + ErrorResult& aRv) +{ + LazyCreateMatrix(mRightViewMatrix, mFrameInfo.mRightView, aCx, aRetval, aRv); +} + +void +VRFrameData::Update(const VRFrameInfo& aFrameInfo) +{ + mFrameInfo = aFrameInfo; + + mLeftProjectionMatrix = nullptr; + mLeftViewMatrix = nullptr; + mRightProjectionMatrix = nullptr; + mRightViewMatrix = nullptr; + + mPose = new VRPose(GetParentObject(), mFrameInfo.mVRState); +} + +void +VRFrameInfo::Update(const gfx::VRDisplayInfo& aInfo, + const gfx::VRHMDSensorState& aState, + float aDepthNear, + float aDepthFar) +{ + mVRState = aState; + + gfx::Quaternion qt; + if (mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Orientation) { + qt.x = mVRState.orientation[0]; + qt.y = mVRState.orientation[1]; + qt.z = mVRState.orientation[2]; + qt.w = mVRState.orientation[3]; + } + gfx::Point3D pos; + if (mVRState.flags & gfx::VRDisplayCapabilityFlags::Cap_Position) { + pos.x = -mVRState.position[0]; + pos.y = -mVRState.position[1]; + pos.z = -mVRState.position[2]; + } + gfx::Matrix4x4 matHead; + matHead.SetRotationFromQuaternion(qt); + matHead.PreTranslate(pos); + + mLeftView = matHead; + mLeftView.PostTranslate(-aInfo.mEyeTranslation[gfx::VRDisplayInfo::Eye_Left]); + + mRightView = matHead; + mRightView.PostTranslate(-aInfo.mEyeTranslation[gfx::VRDisplayInfo::Eye_Right]); + + // Avoid division by zero within ConstructProjectionMatrix + const float kEpsilon = 0.00001f; + if (fabs(aDepthFar - aDepthNear) < kEpsilon) { + aDepthFar = aDepthNear + kEpsilon; + } + + const gfx::VRFieldOfView leftFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Left]; + mLeftProjection = leftFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true); + const gfx::VRFieldOfView rightFOV = aInfo.mEyeFOV[gfx::VRDisplayInfo::Eye_Right]; + mRightProjection = rightFOV.ConstructProjectionMatrix(aDepthNear, aDepthFar, true); +} + +VRFrameInfo::VRFrameInfo() +{ + mVRState.Clear(); +} + +bool +VRFrameInfo::IsDirty() +{ + return mVRState.timestamp == 0; +} + +void +VRFrameInfo::Clear() +{ + mVRState.Clear(); +} + +} // namespace dom +} // namespace mozilla |