Skip to main content
Version: 20 Mar 2024

MagicLeapCamera.cs

Source code

using System;
using System.Collections;
using System.Collections.Generic;
#if UNITY_OPENXR_1_9_0_OR_NEWER
using UnityEngine.XR.OpenXR;
using UnityEngine.XR.OpenXR.Features.MagicLeapSupport;
#endif
#if UNITY_XR_MAGICLEAP_PROVIDER
using MagicLeapXRRenderSettings = UnityEngine.XR.MagicLeap.Rendering.RenderingSettings;
using MagicLeapXRRenderUtility = UnityEngine.XR.MagicLeap.Rendering.RenderingUtility;
#endif

namespace UnityEngine.XR.MagicLeap
{
[ExecuteInEditMode]
[AddComponentMenu("AR/Magic Leap/Magic Leap Camera")]
[DefaultExecutionOrder(ScriptOrder)]
[RequireComponent(typeof(Camera))]
[Obsolete("The MagicLeapCamera component is not recommended for use with OpenXR. It has been deprecated and will be removed in a future release.")]
public sealed class MagicLeapCamera : MonoBehaviour
{
private const int ScriptOrder = -15000;

private new Camera camera;

private List<Transform> transforms = new List<Transform>();
private Unity.Jobs.JobHandle jobHandle;

[SerializeField]
private bool enforceFarClip = false;
[SerializeField]
private Transform stereoConvergencePoint;
[SerializeField]
private bool protectedSurface;

#if UNITY_OPENXR_1_9_0_OR_NEWER
[SerializeField]
private bool vignette;
#endif

[SerializeField]
private bool fixProblemsOnStartup = true;

[SerializeField]
private bool recenterXROriginAtStart = true;

private static readonly float MINIMUM_NEAR_CLIP_METERS = MLDevice.MinimumNearClipDistance;

private static readonly CameraClearFlags DEFAULT_CLEAR_FLAGS = CameraClearFlags.SolidColor;

private static readonly Color DEFAULT_CLEAR_COLOR = new Color(0.0f, 0.0f, 0.0f, 0.0f);

private static readonly Vector3 DEFAULT_CAMERA_POSITION = Vector3.zero;

private static readonly Quaternion DEFAULT_CAMERA_ROTATION = Quaternion.identity;

private static readonly Vector3 DEFAULT_CAMERA_SCALE = Vector3.one;

private static float systemSettingClippingPlaneVal = MLDevice.DefaultNearClipDistance;

public Transform StereoConvergencePoint
{
get => stereoConvergencePoint;
set => stereoConvergencePoint = value;
}

public bool ProtectedSurface
{
get => protectedSurface;
set => protectedSurface = value;
}

public bool EnforceFarClip
{
get => enforceFarClip;
set => enforceFarClip = value;
}

public bool RecenterXROriginAtStart
{
get => recenterXROriginAtStart;
set => recenterXROriginAtStart = value;
}

private void Awake()
{
camera = GetComponent<Camera>();
FixupCamera(fixProblemsOnStartup);

systemSettingClippingPlaneVal = GetNearClipPlaneFromSystem();

if (!Application.isEditor)
{
Debug.Log($"Acquired clipping plane value from system: {systemSettingClippingPlaneVal}");

#if UNITY_OPENXR_1_9_0_OR_NEWER
var renderFeature = OpenXRSettings.Instance.GetFeature<MagicLeapRenderingExtensionsFeature>();
if (renderFeature == null)
return;
renderFeature.FocusDistance = camera.stereoConvergence;
renderFeature.UseProtectedSurface = protectedSurface;
renderFeature.UseVignetteMode = vignette;
#endif
}
}

private IEnumerator Start()
{
yield return new WaitForEndOfFrame();
if (recenterXROriginAtStart)
{
var xro = FindObjectOfType<Unity.XR.CoreUtils.XROrigin>();
xro?.MoveCameraToWorldLocation(Vector3.zero);
xro?.MatchOriginUpCameraForward(Vector3.up, Vector3.forward);
}
}

private void Reset()
{
FixupCamera(fixProblemsOnStartup);
}

private void LateUpdate()
{
if (!(Application.isPlaying && MLDevice.IsMagicLeapLoaderActive()))
{
return;
}

#if UNITY_XR_MAGICLEAP_PROVIDER
MagicLeapXRRenderSettings.cameraScale = MagicLeapXRRenderUtility.GetParentScale(transform);
#endif
ValidateNearClip();
ValidateFarClip();

camera.stereoConvergence = CalculateFocusDistance();
#if UNITY_OPENXR_1_9_0_OR_NEWER
if (!Utils.TryGetOpenXRFeature<MagicLeapRenderingExtensionsFeature>(out var renderFeature))
return;
renderFeature.FocusDistance = camera.stereoConvergence;
renderFeature.UseProtectedSurface = protectedSurface;
renderFeature.UseVignetteMode = vignette;
#elif UNITY_XR_MAGICLEAP_PROVIDER
MagicLeapXRRenderSettings.focusDistance = camera.stereoConvergence;
MagicLeapXRRenderSettings.farClipDistance = camera.farClipPlane;
MagicLeapXRRenderSettings.nearClipDistance = camera.nearClipPlane;
#endif
}

#if !UNITY_EDITOR
private void OnApplicationPause(bool pause)
{
if(!pause)
{
CheckForClipPlaneSettingChange();
}
}
#endif

private void ValidateNearClip()
{
if (camera.nearClipPlane < systemSettingClippingPlaneVal)
{
// reacquire from system just in case
systemSettingClippingPlaneVal = GetNearClipPlaneFromSystem();
if (camera.nearClipPlane < systemSettingClippingPlaneVal)
{
// unity camera nearClipPlane cannot go below what is set in system settings
camera.nearClipPlane = Mathf.Max(camera.nearClipPlane, systemSettingClippingPlaneVal);
}
}
}

public void ValidateFarClip()
{
#if UNITY_XR_MAGICLEAP_PROVIDER
var farClip = camera.farClipPlane;
var max = MagicLeapXRRenderSettings.maxFarClipDistance;
if (farClip > max)
{
if (enforceFarClip)
{
camera.farClipPlane = max;
}
}
#endif
}

private float CalculateFocusDistance()
{
// Get Focus Distance and log warnings if not within the allowed value bounds.
float focusDistance = camera.stereoConvergence;
if (StereoConvergencePoint != null)
{
// From Unity documentation:
// Note that camera space matches OpenGL convention: camera's forward is the negative Z axis.
// This is different from Unity's convention, where forward is the positive Z axis.
Vector3 worldForward = new Vector3(0.0f, 0.0f, -1.0f);
Vector3 camForward = camera.cameraToWorldMatrix.MultiplyVector(worldForward);
camForward = camForward.normalized;

// We are only interested in the focus object's distance to the camera forward tangent plane.
focusDistance = Vector3.Dot(StereoConvergencePoint.position - transform.position, camForward);
}

float nearClip = camera.nearClipPlane;
if (focusDistance < nearClip)
{
focusDistance = nearClip;
}

return focusDistance;
}

public float ClampToClippingPlanes(float value)
{
#if UNITY_XR_MAGICLEAP_PROVIDER
return Mathf.Clamp(value, systemSettingClippingPlaneVal, MagicLeapXRRenderSettings.maxFarClipDistance);
#else
return value;
#endif
}

private void FixupCamera(bool fixIssues)
{
Vector3 cameraScale = GetCameraScale();
float scale;
if (!(Mathf.Approximately(cameraScale.x, cameraScale.y) && Mathf.Approximately(cameraScale.x, cameraScale.z)))
{
// Non uniform scale
scale = (cameraScale.x + cameraScale.y + cameraScale.z) / 3.0f;
}
else
{
scale = cameraScale.x;
}

if ((camera.nearClipPlane / scale) < MINIMUM_NEAR_CLIP_METERS)
{
Debug.LogWarning("[Magic Leap] The near clipping plane of the main camera is closer than " + MINIMUM_NEAR_CLIP_METERS + "m, which can cause artifacts.");

if (fixIssues)
{
camera.nearClipPlane = MINIMUM_NEAR_CLIP_METERS * scale;
}
}

if ((camera.clearFlags != DEFAULT_CLEAR_FLAGS) ||
(camera.backgroundColor != DEFAULT_CLEAR_COLOR))
{
Debug.LogWarning("[Magic Leap] The main camera background is not solid black with an alpha of zero, which is recommended for the Magic Leap platform.");

if (fixIssues)
{
camera.clearFlags = DEFAULT_CLEAR_FLAGS;
camera.backgroundColor = DEFAULT_CLEAR_COLOR;
}
}

if ((camera.transform.localPosition != DEFAULT_CAMERA_POSITION) ||
(camera.transform.localRotation != DEFAULT_CAMERA_ROTATION) ||
(camera.transform.localScale != DEFAULT_CAMERA_SCALE))
{
Debug.LogWarning("[Magic Leap] The main camera isn't at the origin. This will cause head tracking problems.");

if (fixIssues)
{
camera.transform.localPosition = DEFAULT_CAMERA_POSITION;
camera.transform.localRotation = DEFAULT_CAMERA_ROTATION;
camera.transform.localScale = DEFAULT_CAMERA_SCALE;
}
}
}

private Vector3 GetCameraScale()
{
Vector3 scale = Vector3.one;

if (camera && camera == Camera.main)
{
if (transform.parent)
{
scale = transform.parent.lossyScale;
}
}

return scale;
}

private float GetNearClipPlaneFromSystem()
{
if (!Application.isEditor)
{
using (var actClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
var context = actClass.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaClass systemGlobal = new AndroidJavaClass("android.provider.Settings$Secure");

var clipPlaneDist = systemGlobal.CallStatic<float>("getFloat", context.Call<AndroidJavaObject>("getContentResolver"), "clipping_plane_distance", MLDevice.DefaultNearClipDistance);

// if the setting doesn't exist we'll get the default value from MLDevice in meters.
if (clipPlaneDist < 1f)
return clipPlaneDist;

// Otherwise the setting returns centimeters and we need to convert.
return clipPlaneDist / 100f;
}
}

return MLDevice.DefaultNearClipDistance;
}

public void CheckForClipPlaneSettingChange()
{
systemSettingClippingPlaneVal = GetNearClipPlaneFromSystem();
if (!Application.isEditor)
Debug.Log($"Acquired clipping plane value from system: {systemSettingClippingPlaneVal}");
}
}
}