ARRaycastManager.cs
9.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine.XR.ARSubsystems;
namespace UnityEngine.XR.ARFoundation
{
/// <summary>
/// Manages an <c>XRRaycastSubsystem</c>, exposing raycast functionality in ARFoundation. Use this component
/// to raycast against trackables (i.e., detected features in the physical environment) when they do not have
/// a presence in the Physics world.
/// </summary>
[DisallowMultipleComponent]
[RequireComponent(typeof(ARSessionOrigin))]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@3.0/api/UnityEngine.XR.ARFoundation.ARRaycastManager.html")]
public sealed class ARRaycastManager : SubsystemLifecycleManager<XRRaycastSubsystem, XRRaycastSubsystemDescriptor>
{
/// <summary>
/// Cast a ray from a point in screen space against trackables, i.e., detected features such as planes.
/// </summary>
/// <param name="screenPoint">The point, in device screen pixels, from which to cast.</param>
/// <param name="hitResults">Contents are replaced with the raycast results, if successful.</param>
/// <param name="trackableTypes">(Optional) The types of trackables to cast against.</param>
/// <returns>True if the raycast hit a trackable in the <paramref name="trackableTypes"/></returns>
public bool Raycast(
Vector2 screenPoint,
List<ARRaycastHit> hitResults,
TrackableType trackableTypes = TrackableType.All)
{
if (subsystem == null)
return false;
if (hitResults == null)
throw new ArgumentNullException("hitResults");
var nativeHits = m_RaycastViewportDelegate(screenPoint, trackableTypes, Allocator.Temp);
var originTransform = m_SessionOrigin.camera != null ? m_SessionOrigin.camera.transform : m_SessionOrigin.trackablesParent;
return TransformAndDisposeNativeHitResults(nativeHits, hitResults, originTransform.position);
}
/// <summary>
/// Cast a <c>Ray</c> against trackables, i.e., detected features such as planes.
/// </summary>
/// <param name="ray">The <c>Ray</c>, in Unity world space, to cast.</param>
/// <param name="hitResults">Contents are replaced with the raycast results, if successful.</param>
/// <param name="trackableTypes">(Optional) The types of trackables to cast against.</param>
/// <returns>True if the raycast hit a trackable in the <paramref name="trackableTypes"/></returns>
public bool Raycast(
Ray ray,
List<ARRaycastHit> hitResults,
TrackableType trackableTypes = TrackableType.All)
{
if (subsystem == null)
return false;
if (hitResults == null)
throw new ArgumentNullException("hitResults");
var sessionSpaceRay = m_SessionOrigin.trackablesParent.InverseTransformRay(ray);
var nativeHits = m_RaycastRayDelegate(sessionSpaceRay, trackableTypes, Allocator.Temp);
return TransformAndDisposeNativeHitResults(nativeHits, hitResults, ray.origin);
}
static void TransformAndSortRaycastResults(
Transform transform,
NativeArray<XRRaycastHit> nativeHits,
List<ARRaycastHit> managedHits,
Vector3 rayOrigin)
{
foreach (var nativeHit in nativeHits)
{
float distanceInWorldSpace = (nativeHit.pose.position - rayOrigin).magnitude;
managedHits.Add(new ARRaycastHit(nativeHit, distanceInWorldSpace, transform));
}
}
/// <summary>
/// Allows AR managers to register themselves as a raycaster.
/// Raycasters be used as a fallback method if the AR platform does
/// not support raycasting using arbitrary <c>Ray</c>s.
/// </summary>
/// <param name="raycaster">A raycaster implementing the IRaycast interface.</param>
internal void RegisterRaycaster(IRaycaster raycaster)
{
ConstructIfNecessary();
if (!m_Raycasters.Contains(raycaster))
m_Raycasters.Add(raycaster);
}
/// <summary>
/// Unregisters a raycaster previously registered with <see cref="RegisterRaycaster(IRaycaster)"/>.
/// </summary>
/// <param name="raycaster">A raycaster to use in the fallback case.</param>
internal void UnregisterRaycaster(IRaycaster raycaster)
{
if (m_Raycasters != null)
m_Raycasters.Remove(raycaster);
}
protected override void OnAfterStart()
{
if (subsystem.SubsystemDescriptor.supportsViewportBasedRaycast)
{
m_RaycastViewportDelegate = RaycastViewport;
}
else
{
m_RaycastViewportDelegate = RaycastViewportAsRay;
}
if (subsystem.SubsystemDescriptor.supportsWorldBasedRaycast)
{
m_RaycastRayDelegate = RaycastRay;
}
else
{
m_RaycastRayDelegate = RaycastFallback;
}
var raycasters = GetComponents(typeof(IRaycaster));
foreach (var raycaster in raycasters)
RegisterRaycaster((IRaycaster)raycaster);
}
NativeArray<XRRaycastHit> RaycastViewportAsRay(
Vector2 screenPoint,
TrackableType trackableTypeMask,
Allocator allocator)
{
if (m_SessionOrigin.camera == null)
return new NativeArray<XRRaycastHit>(0, allocator);
var worldSpaceRay = m_SessionOrigin.camera.ScreenPointToRay(screenPoint);
var sessionSpaceRay = m_SessionOrigin.trackablesParent.InverseTransformRay(worldSpaceRay);
return m_RaycastRayDelegate(sessionSpaceRay, trackableTypeMask, allocator);
}
NativeArray<XRRaycastHit> RaycastViewport(
Vector2 screenPoint,
TrackableType trackableTypeMask,
Allocator allocator)
{
screenPoint.x = Mathf.Clamp01(screenPoint.x / Screen.width);
screenPoint.y = Mathf.Clamp01(screenPoint.y / Screen.height);
return subsystem.Raycast(screenPoint, trackableTypeMask, allocator);
}
NativeArray<XRRaycastHit> RaycastRay(
Ray ray,
TrackableType trackableTypeMask,
Allocator allocator)
{
return subsystem.Raycast(ray, trackableTypeMask, allocator);
}
static int RaycastHitComparer(ARRaycastHit lhs, ARRaycastHit rhs)
{
return lhs.CompareTo(rhs);
}
NativeArray<XRRaycastHit> RaycastFallback(
Ray ray,
TrackableType trackableTypeMask,
Allocator allocator)
{
s_NativeRaycastHits.Clear();
int count = 0;
foreach (var raycaster in m_Raycasters)
{
var hits = raycaster.Raycast(ray, trackableTypeMask, Allocator.Temp);
if (hits.IsCreated)
{
s_NativeRaycastHits.Add(hits);
count += hits.Length;
}
}
var allHits = new NativeArray<XRRaycastHit>(count, allocator);
int dstIndex = 0;
foreach (var hitArray in s_NativeRaycastHits)
{
NativeArray<XRRaycastHit>.Copy(hitArray, 0, allHits, dstIndex, hitArray.Length);
hitArray.Dispose();
dstIndex += hitArray.Length;
}
return allHits;
}
bool TransformAndDisposeNativeHitResults(
NativeArray<XRRaycastHit> nativeHits,
List<ARRaycastHit> managedHits,
Vector3 rayOrigin)
{
managedHits.Clear();
if (!nativeHits.IsCreated)
return false;
try
{
// Results are in "trackables space", so transform results back into world space
TransformAndSortRaycastResults(m_SessionOrigin.trackablesParent, nativeHits, managedHits, rayOrigin);
managedHits.Sort(s_RaycastHitComparer);
return managedHits.Count > 0;
}
finally
{
nativeHits.Dispose();
}
}
void ConstructIfNecessary()
{
if (m_Raycasters == null)
m_Raycasters = new List<IRaycaster>();
}
void Awake()
{
m_SessionOrigin = GetComponent<ARSessionOrigin>();
ConstructIfNecessary();
}
static Comparison<ARRaycastHit> s_RaycastHitComparer = RaycastHitComparer;
static List<NativeArray<XRRaycastHit>> s_NativeRaycastHits = new List<NativeArray<XRRaycastHit>>();
ARSessionOrigin m_SessionOrigin;
Func<Vector2, TrackableType, Allocator, NativeArray<XRRaycastHit>> m_RaycastViewportDelegate;
Func<Ray, TrackableType, Allocator, NativeArray<XRRaycastHit>> m_RaycastRayDelegate;
List<IRaycaster> m_Raycasters;
}
}