/****************************************************************************** * Copyright (C) Ultraleap, Inc. 2011-2021. * * * * Use subject to the terms of the Apache License 2.0 available at * * http://www.apache.org/licenses/LICENSE-2.0, or another agreement * * between Ultraleap and you, your company or other organization. * ******************************************************************************/ using UnityEngine; namespace Leap.Unity { using Attributes; /// /// Moves the camera to each eye position on pre-render. Only necessary for image /// pass-through (IR viewer) scenarios. /// public class LeapEyeDislocator : MonoBehaviour { [SerializeField] private bool _useCustomBaseline = false; [MinValue(0), Units("MM"), InspectorName("Baseline")] [SerializeField] private float _customBaselineValue = 64; [SerializeField] private bool _showEyePositions = false; [SerializeField] private LeapServiceProvider _provider = null; private Maybe _deviceBaseline = Maybe.None; private bool _hasVisitedPreCull = false; [SerializeField] private Camera _camera = null; private void onDevice(Device device) { _deviceBaseline = Maybe.Some(device.Baseline); } private void OnDestroy() { Camera.onPreCull -= OnCameraPreCull; } private void OnEnable() { if (_provider == null) { enabled = false; return; } Camera.onPreCull -= OnCameraPreCull; Camera.onPreCull += OnCameraPreCull; _provider.OnDeviceSafe += onDevice; } private void OnDisable() { if (_camera == null) return; _camera.ResetStereoViewMatrices(); _provider.OnDeviceSafe -= onDevice; } private void Update() { if (_camera == null) return; _camera.ResetStereoViewMatrices(); _hasVisitedPreCull = false; } private void OnCameraPreCull(Camera cam) { if (_hasVisitedPreCull) { return; } _hasVisitedPreCull = true; Maybe baselineToUse = Maybe.None; if (_useCustomBaseline) { baselineToUse = Maybe.Some(_customBaselineValue); } else { if (_deviceBaseline == Maybe.None) { _provider.OnDeviceSafe += onDevice; } baselineToUse = _deviceBaseline; } float baselineValue; if (baselineToUse.TryGetValue(out baselineValue)) { baselineValue *= 1e-3f; Matrix4x4 leftMat = _camera.GetStereoViewMatrix(Camera.StereoscopicEye.Left); Matrix4x4 rightMat = _camera.GetStereoViewMatrix(Camera.StereoscopicEye.Right); Vector3 leftPos = leftMat.inverse.MultiplyPoint3x4(Vector3.zero); Vector3 rightPos = rightMat.inverse.MultiplyPoint3x4(Vector3.zero); float existingBaseline = Vector3.Distance(leftPos, rightPos); float baselineAdjust = baselineValue - existingBaseline; adjustViewMatrix(Camera.StereoscopicEye.Left, baselineAdjust); adjustViewMatrix(Camera.StereoscopicEye.Right, baselineAdjust); } } private void adjustViewMatrix(Camera.StereoscopicEye eye, float baselineAdjust) { float eyeOffset = eye == Camera.StereoscopicEye.Left ? 1 : -1; Vector3 ipdOffset = eyeOffset * Vector3.right * baselineAdjust * 0.5f; Vector3 providerForwardOffset = Vector3.zero, providerVerticalOffset = Vector3.zero; Quaternion providerRotation = Quaternion.Euler(0f, 180f, 0f); if (_provider is LeapXRServiceProvider || _provider.GetType().BaseType == typeof(LeapXRServiceProvider)) { LeapXRServiceProvider _xrProvider = _provider as LeapXRServiceProvider; providerForwardOffset = Vector3.forward * _xrProvider.deviceOffsetZAxis; providerVerticalOffset = -Vector3.up * _xrProvider.deviceOffsetYAxis; providerRotation = Quaternion.AngleAxis(_xrProvider.deviceTiltXAxis, Vector3.right); } else { Matrix4x4 imageMatWarp = _camera.projectionMatrix * Matrix4x4.TRS(Vector3.zero, providerRotation, Vector3.one) * _camera.projectionMatrix.inverse; Shader.SetGlobalMatrix("_LeapGlobalWarpedOffset", imageMatWarp); } var existingMatrix = _camera.GetStereoViewMatrix(eye); _camera.SetStereoViewMatrix(eye, Matrix4x4.TRS(Vector3.zero, providerRotation, Vector3.one) * Matrix4x4.Translate(providerForwardOffset + ipdOffset) * Matrix4x4.Translate(providerVerticalOffset) * existingMatrix); } private void OnDrawGizmos() { if (_showEyePositions && Application.isPlaying) { Matrix4x4 leftMat = _camera.GetStereoViewMatrix(Camera.StereoscopicEye.Left); Matrix4x4 rightMat = _camera.GetStereoViewMatrix(Camera.StereoscopicEye.Right); Vector3 leftPos = leftMat.inverse.MultiplyPoint3x4(Vector3.zero); Vector3 rightPos = rightMat.inverse.MultiplyPoint3x4(Vector3.zero); Gizmos.color = Color.white; Gizmos.DrawSphere(leftPos, 0.02f); Gizmos.DrawSphere(rightPos, 0.02f); Gizmos.color = Color.blue; Gizmos.DrawLine(leftPos, rightPos); } } } }