using System;
using System.Collections;
using System.Linq;
using Unity.XR.CoreUtils;
using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.Management;
using static UnityEngine.XR.XRDisplaySubsystem;
namespace Unity.XR.XREAL
{
public static class XREALUtility
{
public const string k_Identifier = "com.xreal.xr";
private static Camera mainCamera;
///
/// Gets the main camera, which is either the XR Origin's camera or null if not found.
///
public static Camera MainCamera
{
get
{
if (mainCamera == null)
{
XROrigin origin = FindAnyObjectByType();
mainCamera = (origin != null) ? origin.Camera : null;
}
return mainCamera;
}
}
///
/// Finds an object of the specified type in the scene.
///
/// The type of the object to find.
/// The object if found; otherwise, null.
public static T FindAnyObjectByType() where T : UnityEngine.Object
{
#if UNITY_2023_1_OR_NEWER
return UnityEngine.Object.FindAnyObjectByType();
#else
return UnityEngine.Object.FindObjectOfType();
#endif
}
///
/// Gets the currently active XR loader.
///
/// The active XR loader, or null if no loader is active.
public static XRLoader GetActiveLoader()
{
if (XRGeneralSettings.Instance != null && XRGeneralSettings.Instance.Manager != null)
{
return XRGeneralSettings.Instance.Manager.activeLoader;
}
return null;
}
///
/// Determines whether the XREAL XR loader is currently active.
///
/// True if the XREAL XR loader is active; otherwise, false.
public static bool IsLoaderActive()
{
#if UNITY_EDITOR
var targetGroup = UnityEditor.BuildPipeline.GetBuildTargetGroup(UnityEditor.EditorUserBuildSettings.activeBuildTarget);
var settings = UnityEditor.XR.Management.XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(targetGroup);
return settings != null && settings.Manager.activeLoaders.OfType().Any();
#else
return GetActiveLoader() is XREALXRLoader;
#endif
}
///
/// Gets the loaded subsystem of the specified type from the active loader.
///
/// The type of the subsystem to retrieve.
/// The loaded subsystem of the specified type, or null if not found.
public static T GetLoadedSubsystem() where T : class, ISubsystem
{
XRLoader loader = GetActiveLoader();
if (loader != null)
{
return loader.GetLoadedSubsystem();
}
return null;
}
///
/// Retrieves the XR render parameter for the specified eye.
///
/// The eye index (0 for left, 1 for right).
/// The render parameter for the specified eye.
/// True if the render parameter is successfully retrieved; otherwise, false.
public static bool GetXRRenderParameter(int eye, out XRRenderParameter parameter)
{
parameter = new XRRenderParameter();
XRDisplaySubsystem displaySubsystem = GetLoadedSubsystem();
if (displaySubsystem == null)
return false;
var passCount = displaySubsystem.GetRenderPassCount();
if (passCount == 0)
return false;
int passIndex = passCount == 2 && eye == 1 ? 1 : 0;
displaySubsystem.GetRenderPass(passIndex, out XRRenderPass renderPass);
var parameterCount = renderPass.GetRenderParameterCount();
if (parameterCount == 0)
return false;
int parameterIndex = parameterCount == 2 && eye == 1 ? 1 : 0;
renderPass.GetRenderParameter(MainCamera, parameterIndex, out parameter);
return true;
}
///
/// Determines if the specified transform is a child of the main camera.
///
/// The transform to check.
/// True if the transform is a child of the main camera; otherwise, false.
public static bool IsChildOfCamera(this Transform transform)
{
Transform cameraTransform = MainCamera.transform;
Transform current = transform;
while (current != null)
{
if (current == cameraTransform)
{
return true;
}
current = current.parent;
}
return false;
}
///
/// Calculates the relative pose of the specified transform to the main camera.
///
/// The transform to calculate the relative pose for.
/// The relative pose to the main camera.
public static Pose GetRelativePoseToCamera(this Transform transform)
{
Transform cameraTransform = MainCamera.transform;
Vector3 relativePosition = cameraTransform.InverseTransformPoint(transform.position);
Quaternion relativeRotation = Quaternion.Inverse(cameraTransform.rotation) * transform.rotation;
return new Pose(relativePosition, relativeRotation);
}
///
/// Calculates the relative pose of the specified transform to the parent of the main camera.
///
/// The transform to calculate the relative pose for.
/// The relative pose to the parent of the main camera.
public static Pose GetRelativePoseToCameraParent(this Transform transform)
{
Transform cameraTransform = MainCamera.transform;
if (cameraTransform.parent != null)
{
Vector3 relativePosition = cameraTransform.parent.InverseTransformPoint(transform.position);
Quaternion relativeRotation = Quaternion.Inverse(cameraTransform.parent.rotation) * transform.rotation;
return new Pose(relativePosition, relativeRotation);
}
else
{
return new Pose(transform.position, transform.rotation);
}
}
///
/// Adds or retrieves the specified component from the GameObject.
///
/// The type of the component to add or retrieve.
/// The GameObject to add or retrieve the component from.
/// The component of type T.
public static T AddOrGetComponent(this GameObject gameObject) where T : Component
{
if (gameObject.TryGetComponent(out T component))
{
return component;
}
else
{
return gameObject.AddComponent();
}
}
///
/// Adds or retrieves the specified component from the Component's GameObject.
///
/// The type of the component to add or retrieve.
/// The Component to add or retrieve the component from.
/// The component of type T.
public static T AddOrGetComponent(this Component component) where T : Component
{
return component.gameObject.AddOrGetComponent();
}
///
/// Starts a coroutine that continues until the specified predicate evaluates to true or the timeout expires.
///
/// The MonoBehaviour that starts the coroutine.
/// The condition to evaluate in the coroutine.
/// The maximum time to wait before exiting the coroutine.
/// The Coroutine instance.
public static Coroutine StartCoroutineUntil(this MonoBehaviour monoBehaviour, Func predicate, float timeoutSeconds)
{
return monoBehaviour.StartCoroutine(WaitUntilCondition(predicate, timeoutSeconds));
}
///
/// Waits until the specified condition is met or the timeout expires.
///
/// The condition to evaluate in the coroutine.
/// The maximum time to wait before exiting the coroutine.
/// Null when the condition is met or timeout expires.
private static IEnumerator WaitUntilCondition(Func predicate, float timeoutSeconds)
{
float startTime = Time.time;
while (!predicate())
{
if (Time.time - startTime > timeoutSeconds)
{
yield break;
}
yield return null;
}
}
#if UNITY_ANDROID
static AndroidJavaObject s_UnityActivity;
///
/// Gets the current Unity activity in the Android environment.
///
public static AndroidJavaObject UnityActivity
{
get
{
if (s_UnityActivity == null)
{
using AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
s_UnityActivity = unityPlayer.GetStatic("currentActivity");
}
return s_UnityActivity;
}
}
///
/// Runs the specified action on the Unity Android UI thread.
///
/// The action to execute on the UI thread.
public static void RunOnUiThread(Action action)
{
UnityActivity.Call("runOnUiThread", new AndroidJavaRunnable(action));
}
#endif
}
}