GvrHeadset.cs
13.2 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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
//-----------------------------------------------------------------------
// <copyright file="GvrHeadset.cs" company="Google Inc.">
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections;
using System.ComponentModel;
using Gvr.Internal;
using UnityEngine;
/// <summary>Main entry point for Standalone headset APIs.</summary>
/// <remarks>
/// To use this API, use the GvrHeadset prefab. There can be only one such prefab in a scene, since
/// this is a singleton object.
/// </remarks>
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrHeadset")]
public class GvrHeadset : MonoBehaviour
{
#if UNITY_EDITOR
/// <summary>Whether this app supports Positional Head Tracking in editor play mode.</summary>
/// <remarks>
/// This is a user-controlled field which can be toggled in the inspector for the GvrHeadset.
/// Its value is ignored if there is a connected device running Instant Preview.
/// </remarks>
public static bool editorSupportsPositionalHeadTracking = false;
#endif // UNITY_EDITOR
private static GvrHeadset instance;
private IHeadsetProvider headsetProvider;
private HeadsetState headsetState;
private IEnumerator headsetUpdate;
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
// Delegates for GVR events.
private OnSafetyRegionEvent safetyRegionDelegate;
private OnRecenterEvent recenterDelegate;
/// <summary>Initializes a new instance of the <see cref="GvrHeadset" /> class.</summary>
protected GvrHeadset()
{
headsetState.Initialize();
}
// Delegate definitions.
/// <summary>
/// This delegate is called when the headset crosses the safety region boundary.
/// </summary>
/// <param name="enter">
/// Set to `true` if the safety region is being entered, or `false` if the safety region is
/// being exited.
/// </param>
public delegate void OnSafetyRegionEvent(bool enter);
/// <summary>This delegate is called after the headset is recentered.</summary>
/// <param name="recenterType">Indicates the reason recentering occurred.</param>
/// <param name="recenterFlags">Flags related to recentering. See |GvrRecenterFlags|.</param>
/// <param name="recenteredPosition">The positional offset from the session start pose.</param>
/// <param name="recenteredOrientation">
/// The rotational offset from the session start pose.
/// </param>
public delegate void OnRecenterEvent(GvrRecenterEventType recenterType,
GvrRecenterFlags recenterFlags,
Vector3 recenteredPosition,
Quaternion recenteredOrientation);
#region DELEGATE_HANDLERS
/// <summary>Event handlers for `OnSafetyRegionChange`.</summary>
/// <remarks>Triggered when the safety region has been entered or exited.</remarks>
public static event OnSafetyRegionEvent OnSafetyRegionChange
{
add
{
if (instance != null)
{
instance.safetyRegionDelegate += value;
}
}
remove
{
if (instance != null)
{
instance.safetyRegionDelegate -= value;
}
}
}
/// <summary>Event handlers for `OnRecenter`.</summary>
/// <remarks>Triggered when a recenter command has been issued by the user.</remarks>
public static event OnRecenterEvent OnRecenter
{
add
{
if (instance != null)
{
instance.recenterDelegate += value;
}
}
remove
{
if (instance != null)
{
instance.recenterDelegate -= value;
}
}
}
#endregion // DELEGATE_HANDLERS
#region GVR_HEADSET_PROPERTIES
/// <summary>
/// Gets a value indicating whether this headset supports 6DoF positional tracking.
/// </summary>
/// <value>
/// Value `true` if this headset supports 6DoF positional tracking, or `false` if only 3DoF
/// rotation-based head tracking is supported.
/// </value>
public static bool SupportsPositionalTracking
{
get
{
if (instance == null)
{
return false;
}
try
{
return instance.headsetProvider.SupportsPositionalTracking;
}
catch (Exception e)
{
Debug.LogError("Error reading SupportsPositionalTracking: " + e.Message);
return false;
}
}
}
/// <summary>
/// Gets a value indicating whether this headset provides an Editor Emulator.
/// </summary>
/// <value>
/// Value `true` if this headset provides an Editor Emulator, or `false` otherwise.
/// </value>
public bool ProvidesEditorEmulator
{
get
{
return headsetProvider as EditorHeadsetProvider != null;
}
}
/// <summary>Populates `floorHeight` with the detected height, if one is available.</summary>
/// <remarks>This may be unavailable if the underlying GVR API call fails.</remarks>
/// <returns>
/// Returns `true` if value retrieval was successful, `false` otherwise (depends on tracking
/// state).
/// </returns>
/// <param name="floorHeight">
/// If this call returns `true`, this value is set to the retrieved `floorHeight`. Otherwise
/// leaves the value unchanged.
/// </param>
[SuppressMemoryAllocationError(
IsWarning = true, Reason = "A getter for a float should not allocate.")]
public static bool TryGetFloorHeight(ref float floorHeight)
{
if (instance == null)
{
return false;
}
return instance.headsetProvider.TryGetFloorHeight(ref floorHeight);
}
/// <summary>
/// Populates position and rotation with the last recenter transform, if one is available.
/// </summary>
/// <remarks>This may be unavailable if the underlying GVR API call fails.</remarks>
/// <returns>Returns `true` if value retrieval was successful, `false` otherwise.</returns>
/// <param name="position">
/// If this call returns `true`, this value is set to the retrieved position.
/// </param>
/// <param name="rotation">
/// If this call returns `true`, this value is set to the retrieved rotation.
/// </param>
public static bool TryGetRecenterTransform(ref Vector3 position, ref Quaternion rotation)
{
if (instance == null)
{
return false;
}
return instance.headsetProvider.TryGetRecenterTransform(ref position, ref rotation);
}
/// <summary>Populates `safetyType` with the safety region type, if one is available.</summary>
/// <remarks>
/// Populates `safetyType` with the available safety region feature on the currently-running
/// device. This may be unavailable if the underlying GVR API call fails.
/// </remarks>
/// <returns>Returns `true` if value retrieval was successful, `false` otherwise.</returns>
/// <param name="safetyType">
/// If this call returns `true`, this value is set to the retrieved `safetyType`.
/// </param>
public static bool TryGetSafetyRegionType(ref GvrSafetyRegionType safetyType)
{
if (instance == null)
{
return false;
}
return instance.headsetProvider.TryGetSafetyRegionType(ref safetyType);
}
/// <summary>
/// Populates `innerRadius` with the safety cylinder inner radius, if one is available.
/// </summary>
/// <remarks>
/// This is the radius at which safety management (e.g. safety fog) may cease taking effect.
/// <para>
/// If the safety region is of type `GvrSafetyRegionType.Cylinder`, populates `innerRadius` with
/// the inner radius size of the safety cylinder in meters. Before using, confirm that the
/// safety region type is `GvrSafetyRegionType.Cylinder`. This may be unavailable if the
/// underlying GVR API call fails.
/// </para></remarks>
/// <returns>Returns `true` if value retrieval was successful, `false` otherwise.</returns>
/// <param name="innerRadius">
/// If this call returns `true`, this value is set to the retrieved `innerRadius`.
/// </param>
public static bool TryGetSafetyCylinderInnerRadius(ref float innerRadius)
{
if (instance == null)
{
return false;
}
return instance.headsetProvider.TryGetSafetyCylinderInnerRadius(ref innerRadius);
}
/// <summary>
/// Populates `outerRadius` with the safety cylinder outer radius, if one is available.
/// </summary>
/// <remarks>
/// If the safety region is of type `GvrSafetyRegionType.Cylinder`, populates `outerRadius` with
/// the outer radius size of the safety cylinder in meters. Before using, confirm that the
/// safety region type is `GvrSafetyRegionType.Cylinder`. This may be unavailable if the
/// underlying GVR API call fails.
/// <para>
/// This is the radius at which safety management (e.g. safety fog) may start to take effect.
/// </para></remarks>
/// <returns>Returns `true` if value retrieval was successful, `false` otherwise.</returns>
/// <param name="outerRadius">
/// If this call returns `true`, this value is set to the retrieved `outerRadius`.
/// </param>
public static bool TryGetSafetyCylinderOuterRadius(ref float outerRadius)
{
if (instance == null)
{
return false;
}
return instance.headsetProvider.TryGetSafetyCylinderOuterRadius(ref outerRadius);
}
#endregion // GVR_HEADSET_PROPERTIES
private void Awake()
{
if (instance != null)
{
Debug.LogError("More than one GvrHeadset instance was found in your scene. "
+ "Ensure that there is only one GvrHeadset.");
this.enabled = false;
return;
}
instance = this;
if (headsetProvider == null)
{
headsetProvider = HeadsetProviderFactory.CreateProvider();
}
}
private void OnEnable()
{
if (!SupportsPositionalTracking)
{
return;
}
headsetUpdate = EndOfFrame();
StartCoroutine(headsetUpdate);
}
private void OnDisable()
{
if (!SupportsPositionalTracking)
{
return;
}
if (headsetUpdate != null)
{
StopCoroutine(headsetUpdate);
}
}
private void OnDestroy()
{
if (!SupportsPositionalTracking)
{
return;
}
instance = null;
}
private void UpdateStandalone()
{
// Events are stored in a queue, so poll until we get Invalid.
headsetProvider.PollEventState(ref headsetState);
while (headsetState.eventType != GvrEventType.Invalid)
{
switch (headsetState.eventType)
{
case GvrEventType.Recenter:
if (recenterDelegate != null)
{
recenterDelegate(headsetState.recenterEventType,
(GvrRecenterFlags)headsetState.recenterEventFlags,
headsetState.recenteredPosition,
headsetState.recenteredRotation);
}
break;
case GvrEventType.SafetyRegionEnter:
if (safetyRegionDelegate != null)
{
safetyRegionDelegate(true);
}
break;
case GvrEventType.SafetyRegionExit:
if (safetyRegionDelegate != null)
{
safetyRegionDelegate(false);
}
break;
case GvrEventType.Invalid:
throw new InvalidEnumArgumentException(
"Invalid headset event: " + headsetState.eventType);
default: // Fallthrough, should never get here.
break;
}
headsetProvider.PollEventState(ref headsetState);
}
}
private IEnumerator EndOfFrame()
{
while (true)
{
// This must be done at the end of the frame to ensure that all GameObjects had a chance
// to read transient state (e.g. events, etc) for the current frame before it gets
// reset.
yield return waitForEndOfFrame;
UpdateStandalone();
}
}
}