This repository has been archived by the owner on Jul 26, 2020. It is now read-only.
forked from microsoft/MixedRealityToolkit-Unity
/
ARSessionOrigin.cs
250 lines (229 loc) · 11.6 KB
/
ARSessionOrigin.cs
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
using System;
using UnityEngine.SpatialTracking;
namespace UnityEngine.XR.ARFoundation
{
/// <summary>
/// An <c>ARSessionOrigin</c> is the parent for an AR setup. It contains a <c>Camera</c> and
/// any <c>GameObject</c>s created from detected features, such as planes or point clouds.
/// </summary>
/// <remarks>
/// Session space vs Unity space
///
/// Since an AR device will be used to drive the <c>Camera</c>'s position and rotation,
/// you cannot directly place the <c>Camera</c> at an arbitrary position in the Unity scene.
/// Instead, you should position the <c>ARSessionOrigin</c>. This will make the <c>Camera</c>
/// (and any detected features) relative to that as a result.
///
/// It is important to keep the <c>Camera</c> and detected features in the same space relative to
/// each other (otherwise, detected features like planes won't appear in the correct place relative
/// to the <c>Camera</c>). We call the space relative to the AR device's starting position
/// "session space" or "device space". For example, when the AR session begins, the device may
/// report its position as (0, 0, 0). Detected features, such as planes, will be reported relative
/// to this starting position. The purpose of the <c>ARSessionOrigin</c> is to convert the session space
/// to Unity world space.
///
/// To facilitate this, the <c>ARSessionOrigin</c> creates a new <c>GameObject</c> called "Trackables"
/// as a sibling of its <c>Camera</c>. This should be the parent <c>GameObject</c> for all
/// detected features.
///
/// At runtime, a typical scene graph might look like this:
/// - AR Session Origin
/// - Camera
/// - Trackables
/// - Detected plane 1
/// - Detected plane 2
/// - Point cloud
/// - etc...
///
/// You can access the "trackables" <c>GameObject</c> with <see cref="trackablesParent"/>.
///
/// Note that the <c>localPosition</c> and <c>localRotation</c> of detected trackables
/// remain in real-world meters relative to the AR device's starting position and rotation.
///
/// Scale
///
/// If you want to scale the content rendered by the <c>ARSessionOrigin</c> you should apply
/// the scale to the <c>ARSessionOrigin</c>'s transform. This is preferrable to scaling
/// the content directly as that can have undesirable side-effects. Physics and NavMeshes,
/// for example, do not perform well when scaled very small.
/// </remarks>
[DisallowMultipleComponent]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@1.5/api/UnityEngine.XR.ARFoundation.ARSessionOrigin.html")]
public class ARSessionOrigin : MonoBehaviour
{
[SerializeField]
[Tooltip("The Camera to associate with the AR device.")]
Camera m_Camera;
/// <summary>
/// The <c>Camera</c> to associate with the AR device. It must be a child of this <c>ARSessionOrigin</c>.
/// </summary>
/// <remarks>
/// The <c>Camera</c> should update its position and rotation according to the AR device.
/// This is typically accomplished by adding a <c>TrackedPoseDriver</c> component to the
/// <c>Camera</c>.
/// </remarks>
#if UNITY_EDITOR
public new Camera camera
#else
public Camera camera
#endif
{
get { return m_Camera; }
set { m_Camera = value; }
}
/// <summary>
/// The parent <c>Transform</c> for all "trackables", e.g., planes and feature points.
/// </summary>
public Transform trackablesParent { get; private set; }
GameObject m_ContentOffsetGameObject;
Transform contentOffsetTransform
{
get
{
if (m_ContentOffsetGameObject == null)
{
// Insert a GameObject directly below the rig
m_ContentOffsetGameObject = new GameObject("Content Placement Offset");
m_ContentOffsetGameObject.transform.SetParent(transform, false);
// Re-parent any children of the ARSessionOrigin
for (var i = 0; i < transform.childCount; ++i)
{
var child = transform.GetChild(i);
if (child != m_ContentOffsetGameObject.transform)
{
child.SetParent(m_ContentOffsetGameObject.transform, true);
--i; // Decrement because childCount is also one less.
}
}
}
return m_ContentOffsetGameObject.transform;
}
}
/// <summary>
/// Makes <paramref name="content"/> appear to be placed at <paramref name="position"/> with orientation <paramref name="rotation"/>.
/// </summary>
/// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
/// <param name="position">The position you wish the content to appear at. This could be
/// a position on a detected plane, for example.</param>
/// <param name="rotation">The rotation the content should appear to be in, relative
/// to the <c>Camera</c>.</param>
/// <remarks>
/// This method does not actually change the <c>Transform</c> of content; instead,
/// it updates the <c>ARSessionOrigin</c>'s <c>Transform</c> such that it appears the content
/// is now at the given position and rotation. This is useful for placing AR
/// content onto surfaces when the content itself cannot be moved at runtime.
/// For example, if your content includes terrain or a nav mesh, then it cannot
/// be moved or rotated dynamically.
/// </remarks>
public void MakeContentAppearAt(Transform content, Vector3 position, Quaternion rotation)
{
MakeContentAppearAt(content, position);
MakeContentAppearAt(content, rotation);
}
/// <summary>
/// Makes <paramref name="content"/> appear to be placed at <paramref name="position"/>.
/// </summary>
/// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
/// <param name="position">The position you wish the content to appear at. This could be
/// a position on a detected plane, for example.</param>
/// <remarks>
/// This method does not actually change the <c>Transform</c> of content; instead,
/// it updates the <c>ARSessionOrigin</c>'s <c>Transform</c> such that it appears the content
/// is now at the given position.
/// </remarks>
public void MakeContentAppearAt(Transform content, Vector3 position)
{
if (content == null)
throw new ArgumentNullException("content");
// Adjust the "point of interest" transform to account
// for the actual position we want the content to appear at.
contentOffsetTransform.position += transform.position - position;
// The ARSessionOrigin's position needs to match the content's pivot. This is so
// the entire ARSessionOrigin rotates around the content (so the impression is that
// the content is rotating, not the rig).
transform.position = content.position;
}
/// <summary>
/// Makes <paramref name="content"/> appear to have orientation <paramref name="rotation"/> relative to the <c>Camera</c>.
/// </summary>
/// <param name="content">The <c>Transform</c> of the content you wish to affect.</param>
/// <param name="rotation">The rotation the content should appear to be in, relative
/// to the <c>Camera</c>.</param>
/// <remarks>
/// This method does not actually change the <c>Transform</c> of content; instead,
/// it updates the <c>ARSessionOrigin</c>'s <c>Transform</c> such that it appears the content
/// is in the requested orientation.
/// </remarks>
public void MakeContentAppearAt(Transform content, Quaternion rotation)
{
if (content == null)
throw new ArgumentNullException("content");
// Since we aren't rotating the content, we need to perform the inverse
// operation on the ARSessionOrigin. For example, if we want the
// content to appear to be rotated 90 degrees on the Y axis, we should
// rotate our rig -90 degrees on the Y axis.
transform.rotation = Quaternion.Inverse(rotation) * content.rotation;
}
void Awake()
{
// This will be the parent GameObject for any trackables (such as planes) for which
// we want a corresponding GameObject.
trackablesParent = (new GameObject("Trackables")).transform;
trackablesParent.SetParent(transform, false);
trackablesParent.localPosition = Vector3.zero;
trackablesParent.localRotation = Quaternion.identity;
trackablesParent.localScale = Vector3.one;
if (camera != null)
{
var trackedPoseDriver = camera.GetComponent<TrackedPoseDriver>();
// Warn if not using a TrackedPoseDriver
if (trackedPoseDriver == null)
{
Debug.LogWarningFormat(
"Camera \"{0}\" does not use a Tracked Pose Driver, so its transform " +
"will not be updated by an XR device.", camera.name);
}
// If we are using a TrackedPoseDriver, and the user hasn't chosen "make relative"
// then warn if the camera has a non-identity transform (since it will be overwritten).
else if (!trackedPoseDriver.UseRelativeTransform)
{
var cameraTransform = camera.transform;
if ((cameraTransform.localPosition != Vector3.zero) || (cameraTransform.localRotation != Quaternion.identity))
{
Debug.LogWarningFormat(
"Camera \"{0}\" has a non-identity transform (position = {1}, rotation = {2}). " +
"The camera's local position and rotation will be overwritten by the XR device, " +
"so this starting transform will have no effect. Tick the \"Make Relative\" " +
"checkbox on the camera's Tracked Pose Driver to apply this starting transform.",
camera.name,
cameraTransform.localPosition,
cameraTransform.localRotation);
}
}
}
}
Pose GetCameraOriginPose()
{
var trackedPoseDriver = camera.GetComponent<TrackedPoseDriver>();
if (trackedPoseDriver != null)
{
var localOriginPose = trackedPoseDriver.originPose;
var parent = camera.transform.parent;
if (parent == null)
return localOriginPose;
return parent.TransformPose(localOriginPose);
}
return Pose.identity;
}
void Update()
{
if (camera != null)
{
// Make sure the trackables has the same local transform as the camera's origin
var pose = GetCameraOriginPose();
trackablesParent.position = pose.position;
trackablesParent.rotation = pose.rotation;
}
}
}
}