mixedreality/com.microsoft.mixedreality..../Runtime/Subsystems/RaycastSubsystem.cs

152 lines
6.7 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Collections.Generic;
using Unity.Collections;
using Unity.XR.CoreUtils;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
namespace Microsoft.MixedReality.OpenXR
{
internal class RaycastSubsystem : XRRaycastSubsystem
{
public const string Id = "OpenXR Raycasting";
private class OpenXRProvider : Provider
{
ARPlaneManager m_arPlaneManager = null;
#if USE_ARFOUNDATION_5_OR_NEWER
private XROrigin m_xrOrigin = null;
#else
private ARSessionOrigin m_arSessionOrigin = null;
#endif
public override bool TryAddRaycast(Vector2 screenPoint, float estimatedDistance, out XRRaycast raycast)
{
Debug.LogError("Persistent raycasts are not supported; use single raycasts instead. More information about single and persistent raycasts is available at https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/raycast-manager.html#single-raycasts.");
raycast = default;
return false;
}
public override bool TryAddRaycast(Ray ray, float estimatedDistance, out XRRaycast raycast)
{
Debug.LogError("Persistent raycasts are not supported; use single raycasts instead. More information about single and persistent raycasts is available at https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/raycast-manager.html#single-raycasts.");
raycast = default;
return false;
}
public override void RemoveRaycast(TrackableId trackableId)
{
Debug.LogError("Persistent raycasts are not supported; use single raycasts instead. More information about single and persistent raycasts is available at https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@4.1/manual/raycast-manager.html#single-raycasts.");
}
public override TrackableChanges<XRRaycast> GetChanges(XRRaycast defaultRaycast, Allocator allocator)
{
// Check if the ARPlaneManager is null each frame.
// If the app Raycasts before this GetChanges, we won't forward the request to the ARPlaneManager,
// but if the raycast request is that early on the first frame, planes likely aren't available yet.
if (m_arPlaneManager == null)
{
// Find the active XROrigin (ARSessionOrigin for ARFoundation < 5.0), and its ARPlaneManager.
#if USE_ARFOUNDATION_5_OR_NEWER
if (m_xrOrigin == null || !m_xrOrigin.gameObject.activeInHierarchy)
{
m_xrOrigin = FindObjectUtility.FindFirstObjectByType<XROrigin>(false);
if (m_xrOrigin != null)
{
m_arPlaneManager = m_xrOrigin.GetComponent<ARPlaneManager>();
}
}
#else
if (m_arSessionOrigin == null || !m_arSessionOrigin.gameObject.activeInHierarchy)
{
m_arSessionOrigin = FindObjectUtility.FindFirstObjectByType<ARSessionOrigin>(false);
if (m_arSessionOrigin != null)
{
m_arPlaneManager = m_arSessionOrigin.GetComponent<ARPlaneManager>();
}
}
#endif
}
return base.GetChanges(defaultRaycast, allocator);
}
public override void Stop()
{
m_arPlaneManager = null; // Reset the cached ARPlaneManager.
#if USE_ARFOUNDATION_5_OR_NEWER
m_xrOrigin = null; // Reset the cached XROrigin.
#else
m_arSessionOrigin = null; // Reset the cached ARSessionOrigin.
#endif
}
public override void Destroy()
{
m_arPlaneManager = null; // Reset the cached ARPlaneManager.
#if USE_ARFOUNDATION_5_OR_NEWER
m_xrOrigin = null; // Reset the cached XROrigin.
#else
m_arSessionOrigin = null; // Reset the cached ARSessionOrigin.
#endif
}
public override NativeArray<XRRaycastHit> Raycast(
XRRaycastHit defaultRaycastHit,
Ray ray,
TrackableType trackableTypeMask,
Allocator allocator)
{
// If we don't have a reference to the ARPlaneManager, we don't raycast.
// The ARPlaneManager Raycast is typically only called when it's enabled.
if (m_arPlaneManager == null || !m_arPlaneManager.enabled)
return new NativeArray<XRRaycastHit>(0, allocator);
// Use the ARPlaneManager's raycast - this is the only raycast we currently need to support.
return m_arPlaneManager.Raycast(ray, trackableTypeMask, allocator);
}
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void RegisterDescriptor()
{
XRRaycastSubsystemDescriptor.RegisterDescriptor(new XRRaycastSubsystemDescriptor.Cinfo
{
id = Id,
providerType = typeof(RaycastSubsystem.OpenXRProvider),
subsystemTypeOverride = typeof(RaycastSubsystem),
supportedTrackableTypes = TrackableType.Planes,
supportsTrackedRaycasts = false,
// If this is not supported, ARFoundation will convert the ray to world space, then call the world-based raycast
supportsViewportBasedRaycast = false,
// If this is not supported, ARFoundation will fallback to the ARPlaneManager's raycast.
// On editor remoting, this fallback redirect is broken, so we redirect in Raycast above.
supportsWorldBasedRaycast = true
});
}
};
internal class RaycastSubsystemController : SubsystemController
{
private static List<XRRaycastSubsystemDescriptor> s_RaycastDescriptors = new List<XRRaycastSubsystemDescriptor>();
public RaycastSubsystemController(IOpenXRContext context) : base(context)
{
}
public override void OnSubsystemCreate(ISubsystemPlugin plugin)
{
plugin.CreateSubsystem<XRRaycastSubsystemDescriptor, XRRaycastSubsystem>(s_RaycastDescriptors, RaycastSubsystem.Id);
}
public override void OnSubsystemDestroy(ISubsystemPlugin plugin)
{
plugin.DestroySubsystem<XRRaycastSubsystem>();
}
}
}