235 lines
8.1 KiB
C#
235 lines
8.1 KiB
C#
|
// 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.
|
||
|
|
||
|
using UnityEngine;
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.ComponentModel;
|
||
|
|
||
|
using Gvr.Internal;
|
||
|
|
||
|
/// Main entry point for Standalone headset APIs.
|
||
|
///
|
||
|
/// To use this API, use the GvrHeadset prefab. There can be only one
|
||
|
/// such prefab in a scene.
|
||
|
///
|
||
|
/// This is a singleton object.
|
||
|
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrHeadset")]
|
||
|
public class GvrHeadset : MonoBehaviour {
|
||
|
private static GvrHeadset instance;
|
||
|
|
||
|
private IHeadsetProvider headsetProvider;
|
||
|
private HeadsetState headsetState;
|
||
|
private IEnumerator standaloneUpdate;
|
||
|
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
|
||
|
|
||
|
// Delegates for GVR events.
|
||
|
private OnSafetyRegionEvent safetyRegionDelegate;
|
||
|
private OnRecenterEvent recenterDelegate;
|
||
|
|
||
|
// Delegate definitions.
|
||
|
/// This delegate is called when the headset crosses the safety region boundary.
|
||
|
public delegate void OnSafetyRegionEvent(bool enter);
|
||
|
/// This delegate is called after the headset is recentered.
|
||
|
/// |recenterType| indicates the reason recentering occurred.
|
||
|
/// |recenterFlags| are flags related to recentering. See |GvrRecenterFlags|.
|
||
|
/// |recenteredPosition| is the positional offset from the session start pose.
|
||
|
/// |recenteredOrientation| is the rotational offset from the session start pose.
|
||
|
public delegate void OnRecenterEvent(GvrRecenterEventType recenterType,
|
||
|
GvrRecenterFlags recenterFlags,
|
||
|
Vector3 recenteredPosition,
|
||
|
Quaternion recenteredOrientation);
|
||
|
|
||
|
#region DELEGATE_HANDLERS
|
||
|
public static event OnSafetyRegionEvent OnSafetyRegionChange {
|
||
|
add {
|
||
|
if (instance != null) {
|
||
|
instance.safetyRegionDelegate += value;
|
||
|
}
|
||
|
}
|
||
|
remove {
|
||
|
if (instance != null) {
|
||
|
instance.safetyRegionDelegate -= value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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
|
||
|
/// Returns |true| if the current headset supports positionally tracked, 6DOF head poses.
|
||
|
/// Returns |false| if only rotation-based head poses are supported.
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// If a floor is found, populates floorHeight with the detected height.
|
||
|
/// Otherwise, leaves the value unchanged.
|
||
|
/// Returns true if value retrieval was successful, false otherwise (depends on tracking state).
|
||
|
public static bool TryGetFloorHeight(ref float floorHeight) {
|
||
|
if (instance == null) {
|
||
|
return false;
|
||
|
}
|
||
|
return instance.headsetProvider.TryGetFloorHeight(ref floorHeight);
|
||
|
}
|
||
|
|
||
|
/// If the last recentering transform is available, populates position and rotation with that
|
||
|
/// transform.
|
||
|
/// Returns true if value retrieval was successful, false otherwise (unlikely).
|
||
|
public static bool TryGetRecenterTransform(ref Vector3 position, ref Quaternion rotation) {
|
||
|
if (instance == null) {
|
||
|
return false;
|
||
|
}
|
||
|
return instance.headsetProvider.TryGetRecenterTransform(ref position, ref rotation);
|
||
|
}
|
||
|
|
||
|
/// Populates safetyType with the available safety region feature on the
|
||
|
/// currently-running device.
|
||
|
/// Returns true if value retrieval was successful, false otherwise (unlikely).
|
||
|
public static bool TryGetSafetyRegionType(ref GvrSafetyRegionType safetyType) {
|
||
|
if (instance == null) {
|
||
|
return false;
|
||
|
}
|
||
|
return instance.headsetProvider.TryGetSafetyRegionType(ref safetyType);
|
||
|
}
|
||
|
|
||
|
/// If the safety region is of type GvrSafetyRegionType.Cylinder, populates innerRadius with the
|
||
|
/// inner radius size (where fog starts appearing) of the safety cylinder in meters.
|
||
|
/// Assumes the safety region type has been previously checked by the caller.
|
||
|
/// Returns true if value retrieval was successful, false otherwise (if region type is
|
||
|
/// GvrSafetyRegionType.Invalid).
|
||
|
public static bool TryGetSafetyCylinderInnerRadius(ref float innerRadius) {
|
||
|
if (instance == null) {
|
||
|
return false;
|
||
|
}
|
||
|
return instance.headsetProvider.TryGetSafetyCylinderInnerRadius(ref innerRadius);
|
||
|
}
|
||
|
|
||
|
/// If the safety region is of type GvrSafetyRegionType.Cylinder, populates outerRadius with the
|
||
|
/// outer radius size (where fog is 100% opaque) of the safety cylinder in meters.
|
||
|
/// Assumes the safety region type has been previously checked by the caller.
|
||
|
/// Returns true if value retrieval was successful, false otherwise (if region type is
|
||
|
/// GvrSafetyRegionType.Invalid).
|
||
|
public static bool TryGetSafetyCylinderOuterRadius(ref float outerRadius) {
|
||
|
if (instance == null) {
|
||
|
return false;
|
||
|
}
|
||
|
return instance.headsetProvider.TryGetSafetyCylinderOuterRadius(ref outerRadius);
|
||
|
}
|
||
|
#endregion // GVR_HEADSET_PROPERTIES
|
||
|
|
||
|
private GvrHeadset() {
|
||
|
headsetState.Initialize();
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void OnEnable() {
|
||
|
if (!SupportsPositionalTracking) {
|
||
|
return;
|
||
|
}
|
||
|
standaloneUpdate = EndOfFrame();
|
||
|
StartCoroutine(standaloneUpdate);
|
||
|
}
|
||
|
|
||
|
void OnDisable() {
|
||
|
if (!SupportsPositionalTracking) {
|
||
|
return;
|
||
|
}
|
||
|
StopCoroutine(standaloneUpdate);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
}
|
||
|
}
|
||
|
}
|