Added VR libraries

This commit is contained in:
Chris Midkiff
2018-10-08 23:54:11 -04:00
parent d9eb2a9763
commit 7ce1036e39
1037 changed files with 195630 additions and 348 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 86ebdf1723c09ee499974202114ef39d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,432 @@
// Copyright 2016 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.Collections;
/// Standard implementation for a mathematical model to make the virtual controller approximate the
/// physical location of the Daydream controller.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrArmModel")]
public class GvrArmModel : GvrBaseArmModel, IGvrControllerInputDeviceReceiver {
/// Position of the elbow joint relative to the head before the arm model is applied.
public Vector3 elbowRestPosition = DEFAULT_ELBOW_REST_POSITION;
/// Position of the wrist joint relative to the elbow before the arm model is applied.
public Vector3 wristRestPosition = DEFAULT_WRIST_REST_POSITION;
/// Position of the controller joint relative to the wrist before the arm model is applied.
public Vector3 controllerRestPosition = DEFAULT_CONTROLLER_REST_POSITION;
/// Offset applied to the elbow position as the controller is rotated upwards.
public Vector3 armExtensionOffset = DEFAULT_ARM_EXTENSION_OFFSET;
/// Ratio of the controller's rotation to apply to the rotation of the elbow.
/// The remaining rotation is applied to the wrist's rotation.
[Range(0.0f, 1.0f)]
public float elbowBendRatio = DEFAULT_ELBOW_BEND_RATIO;
/// Offset in front of the controller to determine what position to use when determing if the
/// controller should fade. This is useful when objects are attached to the controller.
[Range(0.0f, 0.4f)]
public float fadeControllerOffset = 0.0f;
/// Controller distance from the front/back of the head after which the controller disappears (meters).
[Range(0.0f, 0.4f)]
public float fadeDistanceFromHeadForward = 0.25f;
/// Controller distance from the left/right of the head after which the controller disappears (meters).
[Range(0.0f, 0.4f)]
public float fadeDistanceFromHeadSide = 0.15f;
/// Controller distance from face after which the tooltips appear (meters).
[Range(0.4f, 0.6f)]
public float tooltipMinDistanceFromFace = 0.45f;
/// When the angle (degrees) between the controller and the head is larger than
/// this value, the tooltips disappear.
/// If the value is 180, then the tooltips are always shown.
/// If the value is 90, the tooltips are only shown when they are facing the camera.
[Range(0, 180)]
public int tooltipMaxAngleFromCamera = 80;
/// If true, the root of the pose is locked to the local position of the player's neck.
public bool isLockedToNeck = false;
/// Represents the controller's position relative to the user's head.
public override Vector3 ControllerPositionFromHead {
get {
return controllerPosition;
}
}
/// Represent the controller's rotation relative to the user's head.
public override Quaternion ControllerRotationFromHead {
get {
return controllerRotation;
}
}
/// The suggested rendering alpha value of the controller.
/// This is to prevent the controller from intersecting the face.
/// The range is always 0 - 1.
public override float PreferredAlpha {
get {
return preferredAlpha;
}
}
/// The suggested rendering alpha value of the controller tooltips.
/// This is to only display the tooltips when the player is looking
/// at the controller, and also to prevent the tooltips from intersecting the
/// player's face.
public override float TooltipAlphaValue {
get {
return tooltipAlphaValue;
}
}
/// Represent the neck's position relative to the user's head.
/// If isLockedToNeck is true, this will be the InputTracking position of the Head node modified
/// by an inverse neck model to approximate the neck position.
/// Otherwise, it is always zero.
public Vector3 NeckPosition {
get {
return neckPosition;
}
}
/// Represent the shoulder's position relative to the user's head.
/// This is not actually used as part of the arm model calculations, and exists for debugging.
public Vector3 ShoulderPosition {
get {
Vector3 shoulderPosition = neckPosition + torsoRotation * Vector3.Scale(SHOULDER_POSITION, handedMultiplier);
return shoulderPosition;
}
}
/// Represent the shoulder's rotation relative to the user's head.
/// This is not actually used as part of the arm model calculations, and exists for debugging.
public Quaternion ShoulderRotation {
get {
return torsoRotation;
}
}
/// Represent the elbow's position relative to the user's head.
public Vector3 ElbowPosition {
get {
return elbowPosition;
}
}
/// Represent the elbow's rotation relative to the user's head.
public Quaternion ElbowRotation {
get {
return elbowRotation;
}
}
/// Represent the wrist's position relative to the user's head.
public Vector3 WristPosition {
get {
return wristPosition;
}
}
/// Represent the wrist's rotation relative to the user's head.
public Quaternion WristRotation {
get {
return wristRotation;
}
}
public GvrControllerInputDevice ControllerInputDevice { get; set; }
protected Vector3 neckPosition;
protected Vector3 elbowPosition;
protected Quaternion elbowRotation;
protected Vector3 wristPosition;
protected Quaternion wristRotation;
protected Vector3 controllerPosition;
protected Quaternion controllerRotation;
protected float preferredAlpha;
protected float tooltipAlphaValue;
/// Multiplier for handedness such that 1 = Right, 0 = Center, -1 = left.
protected Vector3 handedMultiplier;
/// Forward direction of user's torso.
protected Vector3 torsoDirection;
/// Orientation of the user's torso.
protected Quaternion torsoRotation;
// Default values for tuning variables.
public static readonly Vector3 DEFAULT_ELBOW_REST_POSITION = new Vector3(0.195f, -0.5f, 0.005f);
public static readonly Vector3 DEFAULT_WRIST_REST_POSITION = new Vector3(0.0f, 0.0f, 0.25f);
public static readonly Vector3 DEFAULT_CONTROLLER_REST_POSITION = new Vector3(0.0f, 0.0f, 0.05f);
public static readonly Vector3 DEFAULT_ARM_EXTENSION_OFFSET = new Vector3(-0.13f, 0.14f, 0.08f);
public const float DEFAULT_ELBOW_BEND_RATIO = 0.6f;
/// Increases elbow bending as the controller moves up (unitless).
protected const float EXTENSION_WEIGHT = 0.4f;
/// Rest position for shoulder joint.
protected static readonly Vector3 SHOULDER_POSITION = new Vector3(0.17f, -0.2f, -0.03f);
/// Neck offset used to apply the inverse neck model when locked to the head.
protected static readonly Vector3 NECK_OFFSET = new Vector3(0.0f, 0.075f, 0.08f);
/// Amount of normalized alpha transparency to change per second.
protected const float DELTA_ALPHA = 4.0f;
/// Angle ranges the for arm extension offset to start and end (degrees).
protected const float MIN_EXTENSION_ANGLE = 7.0f;
protected const float MAX_EXTENSION_ANGLE = 60.0f;
protected virtual void OnEnable() {
// Register the controller update listener.
GvrControllerInput.OnControllerInputUpdated += OnControllerInputUpdated;
// Force the torso direction to match the gaze direction immediately.
// Otherwise, the controller will not be positioned correctly if the ArmModel was enabled
// when the user wasn't facing forward.
UpdateTorsoDirection(true);
// Update immediately to avoid a frame delay before the arm model is applied.
OnControllerInputUpdated();
}
protected virtual void OnDisable() {
GvrControllerInput.OnControllerInputUpdated -= OnControllerInputUpdated;
}
protected virtual void OnControllerInputUpdated() {
UpdateHandedness();
UpdateTorsoDirection(false);
UpdateNeckPosition();
ApplyArmModel();
UpdateTransparency();
}
protected virtual void UpdateHandedness() {
// Update user handedness if the setting has changed.
if (ControllerInputDevice == null) {
return;
}
// Determine handedness multiplier.
handedMultiplier.Set(0, 1, 1);
if (ControllerInputDevice.IsRightHand) {
handedMultiplier.x = 1.0f;
} else {
handedMultiplier.x = -1.0f;
}
}
protected virtual void UpdateTorsoDirection(bool forceImmediate) {
// Determine the gaze direction horizontally.
Vector3 gazeDirection = GvrVRHelpers.GetHeadForward();
gazeDirection.y = 0.0f;
gazeDirection.Normalize();
// Use the gaze direction to update the forward direction.
if (forceImmediate ||
(ControllerInputDevice != null && ControllerInputDevice.Recentered)) {
torsoDirection = gazeDirection;
} else {
float angularVelocity = ControllerInputDevice != null ? ControllerInputDevice.Gyro.magnitude : 0;
float gazeFilterStrength = Mathf.Clamp((angularVelocity - 0.2f) / 45.0f, 0.0f, 0.1f);
torsoDirection = Vector3.Slerp(torsoDirection, gazeDirection, gazeFilterStrength);
}
// Calculate the torso rotation.
torsoRotation = Quaternion.FromToRotation(Vector3.forward, torsoDirection);
}
protected virtual void UpdateNeckPosition() {
if (isLockedToNeck) {
// Returns the center of the eyes.
// However, we actually want to lock to the center of the head.
neckPosition = GvrVRHelpers.GetHeadPosition();
// Find the approximate neck position by Applying an inverse neck model.
// This transforms the head position to the center of the head and also accounts
// for the head's rotation so that the motion feels more natural.
neckPosition = ApplyInverseNeckModel(neckPosition);
} else {
neckPosition = Vector3.zero;
}
}
protected virtual void ApplyArmModel() {
// Set the starting positions of the joints before they are transformed by the arm model.
SetUntransformedJointPositions();
// Get the controller's orientation.
Quaternion controllerOrientation;
Quaternion xyRotation;
float xAngle;
GetControllerRotation(out controllerOrientation, out xyRotation, out xAngle);
// Offset the elbow by the extension offset.
float extensionRatio = CalculateExtensionRatio(xAngle);
ApplyExtensionOffset(extensionRatio);
// Calculate the lerp rotation, which is used to control how much the rotation of the
// controller impacts each joint.
Quaternion lerpRotation = CalculateLerpRotation(xyRotation, extensionRatio);
CalculateFinalJointRotations(controllerOrientation, xyRotation, lerpRotation);
ApplyRotationToJoints();
}
/// Set the starting positions of the joints before they are transformed by the arm model.
protected virtual void SetUntransformedJointPositions() {
elbowPosition = Vector3.Scale(elbowRestPosition, handedMultiplier);
wristPosition = Vector3.Scale(wristRestPosition, handedMultiplier);
controllerPosition = Vector3.Scale(controllerRestPosition, handedMultiplier);
}
/// Calculate the extension ratio based on the angle of the controller along the x axis.
protected virtual float CalculateExtensionRatio(float xAngle) {
float normalizedAngle = (xAngle - MIN_EXTENSION_ANGLE) / (MAX_EXTENSION_ANGLE - MIN_EXTENSION_ANGLE);
float extensionRatio = Mathf.Clamp(normalizedAngle, 0.0f, 1.0f);
return extensionRatio;
}
/// Offset the elbow by the extension offset.
protected virtual void ApplyExtensionOffset(float extensionRatio) {
Vector3 extensionOffset = Vector3.Scale(armExtensionOffset, handedMultiplier);
elbowPosition += extensionOffset * extensionRatio;
}
/// Calculate the lerp rotation, which is used to control how much the rotation of the
/// controller impacts each joint.
protected virtual Quaternion CalculateLerpRotation(Quaternion xyRotation, float extensionRatio) {
float totalAngle = Quaternion.Angle(xyRotation, Quaternion.identity);
float lerpSuppresion = 1.0f - Mathf.Pow(totalAngle / 180.0f, 6.0f);
float inverseElbowBendRatio = 1.0f - elbowBendRatio;
float lerpValue = inverseElbowBendRatio + elbowBendRatio * extensionRatio * EXTENSION_WEIGHT;
lerpValue *= lerpSuppresion;
return Quaternion.Lerp(Quaternion.identity, xyRotation, lerpValue);
}
/// Determine the final joint rotations relative to the head.
protected virtual void CalculateFinalJointRotations(Quaternion controllerOrientation, Quaternion xyRotation, Quaternion lerpRotation) {
elbowRotation = torsoRotation * Quaternion.Inverse(lerpRotation) * xyRotation;
wristRotation = elbowRotation * lerpRotation;
controllerRotation = torsoRotation * controllerOrientation;
}
/// Apply the joint rotations to the positions of the joints to determine the final pose.
protected virtual void ApplyRotationToJoints() {
elbowPosition = neckPosition + torsoRotation * elbowPosition;
wristPosition = elbowPosition + elbowRotation * wristPosition;
controllerPosition = wristPosition + wristRotation * controllerPosition;
}
/// Transform the head position into an approximate neck position.
protected virtual Vector3 ApplyInverseNeckModel(Vector3 headPosition) {
Quaternion headRotation = GvrVRHelpers.GetHeadRotation();
Vector3 rotatedNeckOffset =
headRotation * NECK_OFFSET - NECK_OFFSET.y * Vector3.up;
headPosition -= rotatedNeckOffset;
return headPosition;
}
/// Controls the transparency of the controller to prevent the controller from clipping through
/// the user's head. Also, controls the transparency of the tooltips so they are only visible
/// when the controller is held up.
protected virtual void UpdateTransparency() {
Vector3 controllerForward = controllerRotation * Vector3.forward;
Vector3 offsetControllerPosition = controllerPosition + (controllerForward * fadeControllerOffset);
Vector3 controllerRelativeToHead = offsetControllerPosition - neckPosition;
Vector3 headForward = GvrVRHelpers.GetHeadForward();
float distanceToHeadForward = Vector3.Scale(controllerRelativeToHead, headForward).magnitude;
Vector3 headRight = Vector3.Cross(headForward, Vector3.up);
float distanceToHeadSide = Vector3.Scale(controllerRelativeToHead, headRight).magnitude;
float distanceToHeadUp = Mathf.Abs(controllerRelativeToHead.y);
bool shouldFadeController = distanceToHeadForward < fadeDistanceFromHeadForward
&& distanceToHeadUp < fadeDistanceFromHeadForward
&& distanceToHeadSide < fadeDistanceFromHeadSide;
// Determine how vertical the controller is pointing.
float animationDelta = DELTA_ALPHA * Time.unscaledDeltaTime;
if (shouldFadeController) {
preferredAlpha = Mathf.Max(0.0f, preferredAlpha - animationDelta);
} else {
preferredAlpha = Mathf.Min(1.0f, preferredAlpha + animationDelta);
}
float dot = Vector3.Dot(controllerRotation * Vector3.up, -controllerRelativeToHead.normalized);
float minDot = (tooltipMaxAngleFromCamera - 90.0f) / -90.0f;
float distToFace = Vector3.Distance(controllerRelativeToHead, Vector3.zero);
if (shouldFadeController
|| distToFace > tooltipMinDistanceFromFace
|| dot < minDot) {
tooltipAlphaValue = Mathf.Max(0.0f, tooltipAlphaValue - animationDelta);
} else {
tooltipAlphaValue = Mathf.Min(1.0f, tooltipAlphaValue + animationDelta);
}
}
/// Get the controller's orientation.
protected void GetControllerRotation(out Quaternion rotation, out Quaternion xyRotation, out float xAngle) {
// Find the controller's orientation relative to the player.
rotation = ControllerInputDevice != null ? ControllerInputDevice.Orientation : Quaternion.identity;
rotation = Quaternion.Inverse(torsoRotation) * rotation;
// Extract just the x rotation angle.
Vector3 controllerForward = rotation * Vector3.forward;
xAngle = 90.0f - Vector3.Angle(controllerForward, Vector3.up);
// Remove the z rotation from the controller.
xyRotation = Quaternion.FromToRotation(Vector3.forward, controllerForward);
}
#if UNITY_EDITOR
protected virtual void OnDrawGizmosSelected() {
if (!enabled) {
return;
}
if (transform.parent == null) {
return;
}
Vector3 worldShoulder = transform.parent.TransformPoint(ShoulderPosition);
Vector3 worldElbow = transform.parent.TransformPoint(elbowPosition);
Vector3 worldwrist = transform.parent.TransformPoint(wristPosition);
Vector3 worldcontroller = transform.parent.TransformPoint(controllerPosition);
Gizmos.color = Color.red;
Gizmos.DrawSphere(worldShoulder, 0.02f);
Gizmos.DrawLine(worldShoulder, worldElbow);
Gizmos.color = Color.green;
Gizmos.DrawSphere(worldElbow, 0.02f);
Gizmos.DrawLine(worldElbow, worldwrist);
Gizmos.color = Color.cyan;
Gizmos.DrawSphere(worldwrist, 0.02f);
Gizmos.color = Color.blue;
Gizmos.DrawSphere(worldcontroller, 0.02f);
}
#endif // UNITY_EDITOR
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7dda4bb2d5509e44e978d2bf56e25d7c
timeCreated: 1471566115
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,42 @@
// 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 System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// Interface for a mathematical model that uses the orientation and location
/// of the physical controller, and predicts the location of the controller and pointer
/// to determine where to place the controller model within the scene.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrBaseArmModel")]
public abstract class GvrBaseArmModel : MonoBehaviour {
/// Vector to represent the controller's location relative to
/// the user's head position.
public abstract Vector3 ControllerPositionFromHead { get; }
/// Quaternion to represent the controller's rotation relative to
/// the user's head position.
public abstract Quaternion ControllerRotationFromHead { get; }
/// The suggested rendering alpha value of the controller.
/// This is to prevent the controller from intersecting the face.
/// The range is always 0 - 1.
public abstract float PreferredAlpha { get; }
/// The suggested rendering alpha value of the controller tooltips.
/// This is to only display the tooltips when the player is looking
/// at the controller, and also to prevent the tooltips from intersecting the
/// player's face.
public abstract float TooltipAlphaValue { get; }
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b621d217eade547b4841c4471106b6e5
timeCreated: 1495573504
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
// 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.
public interface IGvrArmModelReceiver {
GvrBaseArmModel ArmModel { get; set; }
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cb01f40fa958548a5b1f92a685e6d46d
timeCreated: 1495576213
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,630 @@
// 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 Gvr.Internal;
/// Represents a controller's current connection state.
/// All values and semantics below (except for Error) are
/// from gvr_types.h in the GVR C API.
public enum GvrConnectionState {
/// Indicates that an error has occurred.
Error = -1,
/// Indicates a controller is disconnected.
Disconnected = 0,
/// Indicates that the device is scanning for controllers.
Scanning = 1,
/// Indicates that the device is connecting to a controller.
Connecting = 2,
/// Indicates that the device is connected to a controller.
Connected = 3,
}
/// Represents the status of the controller API.
/// Values and semantics from gvr_types.h in the GVR C API.
public enum GvrControllerApiStatus {
/// A Unity-localized error occurred.
/// This is the only value that isn't in gvr_types.h.
Error = -1,
/// API is happy and healthy. This doesn't mean any controllers are
/// connected, it just means that the underlying service is working
/// properly.
Ok = 0,
/// Any other status represents a permanent failure that requires
/// external action to fix:
/// API failed because this device does not support controllers (API is too
/// low, or other required feature not present).
Unsupported = 1,
/// This app was not authorized to use the service (e.g., missing permissions,
/// the app is blacklisted by the underlying service, etc).
NotAuthorized = 2,
/// The underlying VR service is not present.
Unavailable = 3,
/// The underlying VR service is too old, needs upgrade.
ApiServiceObsolete = 4,
/// The underlying VR service is too new, is incompatible with current client.
ApiClientObsolete = 5,
/// The underlying VR service is malfunctioning. Try again later.
ApiMalfunction = 6,
}
/// Represents a controller's current battery level.
/// Values and semantics from gvr_types.h in the GVR C API.
public enum GvrControllerBatteryLevel {
/// A Unity-localized error occurred.
/// This is the only value that isn't in gvr_types.h.
Error = -1,
/// The battery state is currently unreported.
Unknown = 0,
/// Equivalent to 1 out of 5 bars on the battery indicator.
CriticalLow = 1,
/// Equivalent to 2 out of 5 bars on the battery indicator.
Low = 2,
/// Equivalent to 3 out of 5 bars on the battery indicator.
Medium = 3,
/// Equivalent to 4 out of 5 bars on the battery indicator.
AlmostFull = 4,
/// Equivalent to 5 out of 5 bars on the battery indicator.
Full = 5,
}
/// Represents controller buttons.
/// Values 0-9 from gvr_types.h in the GVR C API.
/// Value 31 not represented in the C API.
public enum GvrControllerButton {
/// Button under the touch pad. Formerly known as Click.
TouchPadButton = 1 << 1,
/// Touch pad touching indicator.
TouchPadTouch = 1 << 31,
/// General application button.
App = 1 << 3,
/// System button. Formerly known as Home.
System = 1 << 2,
/// Buttons reserved for future use. Subject to name change.
Reserved0 = 1 << 6,
Reserved1 = 1 << 7,
Reserved2 = 1 << 8,
}
/// Represents controller handedness.
public enum GvrControllerHand {
Right,
Left,
Dominant, // Alias for dominant hand as specified by `GvrSettings.Handedness`.
NonDominant, // Alias for non-dominant hand.
}
/// Main entry point for the Daydream controller API.
///
/// To use this API, add this script to a game object in your scene, or use the
/// **GvrControllerMain** prefab.
///
/// This is a singleton object. There can only be one object with this script in your scene.
///
/// To access a controller's state, get a device from `GvrControllerInput.GetDevice` then
/// query it for state. For example, to the dominant controller's current orientation, use
/// `GvrControllerInput.GetDevice(GvrControllerHand.Dominant).Orientation`.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerInput")]
public class GvrControllerInput : MonoBehaviour {
private static GvrControllerInputDevice[] instances = new GvrControllerInputDevice[0];
private static IControllerProvider controllerProvider;
private static GvrSettings.UserPrefsHandedness handedness;
private static Action onDevicesChangedInternal;
/// Event handler for receiving button, touchpad, and IMU updates from the controllers.
/// Use this handler to update app state based on controller input.
public static event Action OnControllerInputUpdated;
/// Event handler for receiving a second notification callback, after all
/// `OnControllerInputUpdated` events have fired.
public static event Action OnPostControllerInputUpdated;
/// Event handler for when the connection state of a controller changes.
public delegate void OnStateChangedEvent(GvrConnectionState state, GvrConnectionState oldState);
/// Event handler for when controller devices have changed. Any code that stores a
/// `GvrControllerInputDevice` should get a new device instance from `GetDevice`.
/// Existing `GvrControllerInputDevice`s will be marked invalid and will log errors
/// when used. Event handlers are called immediately when added.
public static event Action OnDevicesChanged {
add {
onDevicesChangedInternal += value;
value();
}
remove {
onDevicesChangedInternal -= value;
}
}
/// Event handler for when the connection state of the dominant controller changes.
[System.Obsolete("Replaced by GvrControllerInputDevice.OnStateChangedEvent.")]
public static event OnStateChangedEvent OnStateChanged {
add {
if (instances.Length > 0) {
instances[0].OnStateChanged += value;
} else {
Debug.LogError("GvrControllerInput: Adding OnStateChanged event before instance created.");
}
}
remove {
if (instances.Length > 0) {
instances[0].OnStateChanged -= value;
} else {
Debug.LogError("GvrControllerInput: Removing OnStateChanged event before instance created.");
}
}
}
public enum EmulatorConnectionMode {
OFF,
USB,
WIFI,
}
/// Indicates how we connect to the controller emulator.
[GvrInfo("Hold Shift to use the Mouse as the dominant controller.\n\n" +
"Controls: Shift +\n" +
" • Move Mouse = Change Orientation\n" +
" • Left Mouse Button = ClickButton\n" +
" • Right Mouse Button = AppButton\n" +
" • Middle Mouse Button = HomeButton/Recenter\n" +
" • Ctrl = IsTouching\n" +
" • Ctrl + Move Mouse = Change TouchPos", 8)]
[Tooltip("How to connect to the emulator: USB cable (recommended) or WIFI.")]
public EmulatorConnectionMode emulatorConnectionMode = EmulatorConnectionMode.USB;
/// Returns a controller device for the specified hand.
public static GvrControllerInputDevice GetDevice(GvrControllerHand hand) {
if (instances.Length == 0) {
return null;
}
// Remap Right and Left to Dominant or NonDominant according to settings handedness.
if (hand == GvrControllerHand.Left || hand == GvrControllerHand.Right) {
if ((int)hand != (int)handedness) {
hand = GvrControllerHand.NonDominant;
} else {
hand = GvrControllerHand.Dominant;
}
}
if (hand == GvrControllerHand.NonDominant) {
return instances[1];
} else {
// Dominant is always controller 0.
return instances[0];
}
}
/// Returns the dominant controller's current connection state. Returns
/// `GvrConnectionState.Error` if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.State.")]
public static GvrConnectionState State {
get {
if (instances.Length == 0) {
return GvrConnectionState.Error;
}
return instances[0].State;
}
}
/// Returns the status of the controller API. Returns
/// `GvrControllerApiStatus.Error` if `GvrControllerInput` is uninitialized.
public static GvrControllerApiStatus ApiStatus {
get {
if (instances.Length == 0) {
return GvrControllerApiStatus.Error;
}
return instances[0].ApiStatus;
}
}
/// Returns true if battery status is supported. Returns false if
/// `GvrControllerInput` is uninitialized.
public static bool SupportsBatteryStatus {
get {
if (controllerProvider == null) {
return false;
}
return controllerProvider.SupportsBatteryStatus;
}
}
/// Returns the dominant controller's current orientation in space, as a quaternion.
/// Returns `Quaternion.identity` if `GvrControllerInput` is uninitialized.
/// The rotation is provided in 'orientation space' which means the rotation is given relative
/// to the last time the user recentered their controllers. To make a game object in your scene
/// have the same orientation as the dominant controller, simply assign this quaternion to the
/// object's `transform.rotation`. To match the relative rotation, use `transform.localRotation`
/// instead.
[System.Obsolete("Replaced by GvrControllerInputDevice.Orientation.")]
public static Quaternion Orientation {
get {
if (instances.Length == 0) {
return Quaternion.identity;
}
return instances[0].Orientation;
}
}
/// Returns the dominant controller's current angular speed in radians per second, using the right-hand
/// rule (positive means a right-hand rotation about the given axis), as measured by the
/// controller's gyroscope. Returns `Vector3.zero` if `GvrControllerInput` is uninitialized.
/// The controller's axes are:
/// - X points to the right,
/// - Y points perpendicularly up from the controller's top surface
/// - Z lies along the controller's body, pointing towards the front
[System.Obsolete("Replaced by GvrControllerInputDevice.Gyro.")]
public static Vector3 Gyro {
get {
if (instances.Length == 0) {
return Vector3.zero;
}
return instances[0].Gyro;
}
}
/// Returns the dominant controller's current acceleration in meters per second squared.
/// Returns `Vector3.zero` if `GvrControllerInput` is uninitialized.
/// The controller's axes are:
/// - X points to the right,
/// - Y points perpendicularly up from the controller's top surface
/// - Z lies along the controller's body, pointing towards the front
/// Note that gravity is indistinguishable from acceleration, so when the controller is resting
/// on a surface, expect to measure an acceleration of 9.8 m/s^2 on the Y axis. The accelerometer
/// reading will be zero on all three axes only if the controller is in free fall, or if the user
/// is in a zero gravity environment like a space station.
[System.Obsolete("Replaced by GvrControllerInputDevice.Accel.")]
public static Vector3 Accel {
get {
if (instances.Length == 0) {
return Vector3.zero;
}
return instances[0].Accel;
}
}
/// Returns true while the user is touching the dominant controller's touchpad. Returns
/// false if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.TouchPadTouch).")]
public static bool IsTouching {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButton(GvrControllerButton.TouchPadTouch);
}
}
/// Returns true in the frame the user starts touching the dominant controller's touchpad.
/// Returns false if `GvrControllerInput` is uninitialized.
/// Every TouchDown event is guaranteed to be followed by exactly one TouchUp event in a
/// later frame. Also, TouchDown and TouchUp will never both be true in the same frame.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.TouchPadTouch).")]
public static bool TouchDown {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButtonDown(GvrControllerButton.TouchPadTouch);
}
}
/// Returns true the frame after the user stops touching the dominant controller's touchpad.
/// Returns false if `GvrControllerInput` is uninitialized.
/// Every TouchUp event is guaranteed to be preceded by exactly one TouchDown event in an
/// earlier frame. Also, TouchDown and TouchUp will never both be true in the same frame.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonUp(GvrControllerButton.TouchPadTouch).")]
public static bool TouchUp {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButtonUp(GvrControllerButton.TouchPadTouch);
}
}
/// Position of the dominant controller's current touch, if touching the touchpad.
/// Returns `Vector2(0.5f, 0.5f)` if `GvrControllerInput` is uninitialized.
/// If not touching, this is the position of the last touch (when the finger left the touchpad).
/// The X and Y range is from 0 to 1.
/// (0, 0) is the top left of the touchpad and (1, 1) is the bottom right of the touchpad.
[System.Obsolete("Obsolete. Migrate to the center-relative GvrControllerInputDevice.TouchPos.")]
public static Vector2 TouchPos {
get {
if (instances.Length == 0) {
return new Vector2(0.5f,0.5f);
}
Vector2 touchPos = instances[0].TouchPos;
touchPos.x = (touchPos.x / 2.0f) + 0.5f;
touchPos.y = (-touchPos.y / 2.0f) + 0.5f;
return touchPos;
}
}
/// Position of the dominant controller's current touch, if touching the touchpad.
/// Returns `Vector2.zero` if `GvrControllerInput` is uninitialized.
/// If not touching, this is the position of the last touch (when the finger left the touchpad).
/// The X and Y range is from -1 to 1. (-.707,-.707) is bottom left, (.707,.707) is upper right.
/// (0, 0) is the center of the touchpad.
/// The magnitude of the touch vector is guaranteed to be <= 1.
[System.Obsolete("Replaced by GvrControllerInputDevice.TouchPos.")]
public static Vector2 TouchPosCentered {
get {
if (instances.Length == 0) {
return Vector2.zero;
}
return instances[0].TouchPos;
}
}
[System.Obsolete("Use Recentered to detect when user has completed the recenter gesture.")]
public static bool Recentering {
get {
return false;
}
}
/// Returns true if the user just completed the recenter gesture. Returns false if
/// `GvrControllerInput` is uninitialized. The headset and the dominant controller's
/// orientation are now being reported in the new recentered coordinate system. This
/// is an event flag (it is true for only one frame after the event happens, then
/// reverts to false).
public static bool Recentered {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].Recentered;
}
}
/// Returns true while the user holds down the dominant controller's touchpad button.
/// Returns false if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.TouchPadButton).")]
public static bool ClickButton {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButton(GvrControllerButton.TouchPadButton);
}
}
/// Returns true in the frame the user starts pressing down the dominant controller's
/// touchpad button. Returns false if `GvrControllerInput` is uninitialized. Every
/// ClickButtonDown event is guaranteed to be followed by exactly one ClickButtonUp
/// event in a later frame. Also, ClickButtonDown and ClickButtonUp will never both be
/// true in the same frame.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.TouchPadButton).")]
public static bool ClickButtonDown {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButtonDown(GvrControllerButton.TouchPadButton);
}
}
/// Returns true the frame after the user stops pressing down the dominant controller's
/// touchpad button. Returns false if `GvrControllerInput` is uninitialized. Every
/// ClickButtonUp event is guaranteed to be preceded by exactly one ClickButtonDown
/// event in an earlier frame. Also, ClickButtonDown and ClickButtonUp will never both
/// be true in the same frame.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonUp(GvrControllerButton.TouchPadButton).")]
public static bool ClickButtonUp {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButtonUp(GvrControllerButton.TouchPadButton);
}
}
/// Returns true while the user holds down the dominant controller's app button. Returns
/// false if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.App).")]
public static bool AppButton {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButton(GvrControllerButton.App);
}
}
/// Returns true in the frame the user starts pressing down the dominant controller's app button.
/// Returns false if `GvrControllerInput` is uninitialized. Every AppButtonDown event is
/// guaranteed to be followed by exactly one AppButtonUp event in a later frame.
/// Also, AppButtonDown and AppButtonUp will never both be true in the same frame.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.App).")]
public static bool AppButtonDown {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButtonDown(GvrControllerButton.App);
}
}
/// Returns true the frame after the user stops pressing down the dominant controller's app button.
/// Returns false if `GvrControllerInput` is uninitialized. Every AppButtonUp event is guaranteed
/// to be preceded by exactly one AppButtonDown event in an earlier frame. Also, AppButtonDown
/// and AppButtonUp will never both be true in the same frame.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonUp(GvrControllerButton.App).")]
public static bool AppButtonUp {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButtonUp(GvrControllerButton.App);
}
}
/// Returns true in the frame the user starts pressing down the dominant controller's system button.
/// Returns false if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.System).")]
public static bool HomeButtonDown {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButtonDown(GvrControllerButton.System);
}
}
/// Returns true while the user holds down the dominant controller's system button.
/// Returns false if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.System).")]
public static bool HomeButtonState {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].GetButton(GvrControllerButton.System);
}
}
/// If the dominant controller's state == GvrConnectionState.Error, this contains details about
/// the error. If `GvrControllerInput` is uninitialized this returns an error string describing
/// the uninitialized state.
[System.Obsolete("Replaced by GvrControllerInputDevice.ErrorDetails.")]
public static string ErrorDetails {
get {
if (instances.Length > 0) {
return instances[0].ErrorDetails;
} else {
return "No GvrControllerInput initialized instance found in scene. It may be missing, or it might "
+ "not have initialized yet.";
}
}
}
/// Returns the GVR C library controller state pointer (gvr_controller_state*) for the dominant
/// controller. Returns `IntPtr.Zero` if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.StatePtr.")]
public static IntPtr StatePtr {
get {
if (instances.Length == 0) {
return IntPtr.Zero;
}
return instances[0].StatePtr;
}
}
/// Returns true if the dominant controller is currently being charged. Returns false if
/// `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.IsCharging.")]
public static bool IsCharging {
get {
if (instances.Length == 0) {
return false;
}
return instances[0].IsCharging;
}
}
/// Returns the dominant controller's current battery charge level. Returns
/// `GvrControllerBatteryLevel.Error` if `GvrControllerInput` is uninitialized.
[System.Obsolete("Replaced by GvrControllerInputDevice.BatteryLevel.")]
public static GvrControllerBatteryLevel BatteryLevel {
get {
if (instances.Length == 0) {
return GvrControllerBatteryLevel.Error;
}
return instances[0].BatteryLevel;
}
}
void Awake() {
if (instances.Length > 0) {
Debug.LogError("More than one active GvrControllerInput instance was found in your scene. "
+ "Ensure that there is only one GvrControllerInput.");
this.enabled = false;
return;
}
if (controllerProvider == null) {
controllerProvider = ControllerProviderFactory.CreateControllerProvider(this);
}
handedness = GvrSettings.Handedness;
int controllerCount = 2;
instances = new GvrControllerInputDevice[controllerCount];
for (int i=0; i<controllerCount; i++) {
instances[i] = new GvrControllerInputDevice(controllerProvider, i);
}
if (onDevicesChangedInternal != null) {
onDevicesChangedInternal();
}
// Keep screen on here, since GvrControllerInput must be in any GVR scene in order to enable
// controller capabilities.
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
void Update() {
foreach (var instance in instances) {
if (instance != null) {
instance.Update();
}
}
if (OnControllerInputUpdated != null) {
OnControllerInputUpdated();
}
if (OnPostControllerInputUpdated != null) {
OnPostControllerInputUpdated();
}
}
void OnDestroy() {
foreach (var instance in instances) {
// Ensure this device will error if used again.
instance.Invalidate();
}
instances = new GvrControllerInputDevice[0];
if (onDevicesChangedInternal != null) {
onDevicesChangedInternal();
}
}
void OnApplicationPause(bool paused) {
if (null == controllerProvider) return;
if (paused) {
controllerProvider.OnPause();
} else {
handedness = GvrSettings.Handedness;
controllerProvider.OnResume();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 41251d5f89d5546bb9d8ba907686b71f
timeCreated: 1495645608
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: -31123
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,250 @@
// Copyright 2018 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 Gvr.Internal;
/// Device instance of the Daydream controller API.
public class GvrControllerInputDevice {
private IControllerProvider controllerProvider;
private int controllerId;
private ControllerState controllerState = new ControllerState();
private Vector2 touchPosCentered = Vector2.zero;
private int lastUpdatedFrameCount = -1;
private bool valid;
/// Event handler for when the connection state of the controller changes.
public event GvrControllerInput.OnStateChangedEvent OnStateChanged;
internal GvrControllerInputDevice(IControllerProvider provider, int controller_id) {
controllerProvider = provider;
controllerId = controller_id;
valid = true;
}
internal void Invalidate() {
valid = false;
}
public bool IsDominantHand {
get {
return controllerId == 0;
}
}
public bool IsRightHand {
get {
if (controllerId == 0) {
return GvrSettings.Handedness == GvrSettings.UserPrefsHandedness.Right;
} else {
return GvrSettings.Handedness == GvrSettings.UserPrefsHandedness.Left;
}
}
}
/// Returns the controller's current connection state.
public GvrConnectionState State {
get {
Update();
return controllerState.connectionState;
}
}
/// Returns the API status of the current controller state.
public GvrControllerApiStatus ApiStatus {
get {
Update();
return controllerState.apiStatus;
}
}
/// Returns the controller's current orientation in space, as a quaternion.
/// The rotation is provided in 'orientation space' which means the rotation is given relative
/// to the last time the user recentered their controller. To make a game object in your scene
/// have the same orientation as the controller, simply assign this quaternion to the object's
/// `transform.rotation`. To match the relative rotation, use `transform.localRotation` instead.
public Quaternion Orientation {
get {
Update();
return controllerState.orientation;
}
}
public Vector3 Position {
get {
Update();
return controllerState.position;
}
}
/// Returns the controller's current angular speed in radians per second, using the right-hand
/// rule (positive means a right-hand rotation about the given axis), as measured by the
/// controller's gyroscope.
/// The controller's axes are:
/// - X points to the right,
/// - Y points perpendicularly up from the controller's top surface
/// - Z lies along the controller's body, pointing towards the front
public Vector3 Gyro {
get {
Update();
return controllerState.gyro;
}
}
/// Returns the controller's current acceleration in meters per second squared.
/// The controller's axes are:
/// - X points to the right,
/// - Y points perpendicularly up from the controller's top surface
/// - Z lies along the controller's body, pointing towards the front
/// Note that gravity is indistinguishable from acceleration, so when the controller is resting
/// on a surface, expect to measure an acceleration of 9.8 m/s^2 on the Y axis. The accelerometer
/// reading will be zero on all three axes only if the controller is in free fall, or if the user
/// is in a zero gravity environment like a space station.
public Vector3 Accel {
get {
Update();
return controllerState.accel;
}
}
/// Position of the current touch, if touching the touchpad.
/// If not touching, this is the position of the last touch (when the finger left the touchpad).
/// The X and Y range is from -1.0 to 1.0. (0, 0) is the center of the touchpad.
/// (-.707, -.707) is bottom left, (.707, .707) is upper right.
/// The magnitude of the touch vector is guaranteed to be <= 1.0.
public Vector2 TouchPos {
get {
Update();
return touchPosCentered;
}
}
/// Returns true if the user just completed the recenter gesture. The headset and
/// the controller's orientation are now being reported in the new recentered
/// coordinate system. This is an event flag (it is true for only one frame
/// after the event happens, then reverts to false).
public bool Recentered {
get {
Update();
return controllerState.recentered;
}
}
/// Returns true if the user is holding down any of the buttons specified in `buttons`.
/// GvrControllerButton types can be OR-ed together to check for multiple buttons at once.
public bool GetButton(GvrControllerButton buttons) {
Update();
return (controllerState.buttonsState & buttons) != 0;
}
/// Returns true in the frame the user starts pressing down any of the buttons specified
/// in `buttons`. For an individual button enum, every ButtonDown event is guaranteed to be
/// followed by exactly one ButtonUp event in a later frame. Also, ButtonDown and ButtonUp
/// will never both be true in the same frame for an individual button. Using multiple button
/// enums OR'ed together can result in multiple ButtonDowns before a ButtonUp.
public bool GetButtonDown(GvrControllerButton buttons) {
Update();
return (controllerState.buttonsDown & buttons) != 0;
}
/// Returns true the frame after the user stops pressing down any of the buttons specified
/// in `buttons`. For an individual button enum, every ButtonUp event is guaranteed to be
/// preceded by exactly one ButtonDown event in an earlier frame. Also, ButtonDown and
/// ButtonUp will never both be true in the same frame for an individual button. Using
/// multiple button enums OR'ed together can result in multiple ButtonUps after multiple
/// ButtonDowns.
public bool GetButtonUp(GvrControllerButton buttons) {
Update();
return (controllerState.buttonsUp & buttons) != 0;
}
/// If State == GvrConnectionState.Error, this contains details about the error.
public string ErrorDetails {
get {
Update();
return controllerState.connectionState == GvrConnectionState.Error ?
controllerState.errorDetails : "";
}
}
/// Returns the GVR C library controller state pointer (gvr_controller_state*).
public IntPtr StatePtr {
get {
Update();
return controllerState.gvrPtr;
}
}
/// Returns true if the controller is currently being charged.
public bool IsCharging {
get {
Update();
return controllerState.isCharging;
}
}
/// Returns the controller's current battery charge level.
public GvrControllerBatteryLevel BatteryLevel {
get {
Update();
return controllerState.batteryLevel;
}
}
internal void Update() {
if (lastUpdatedFrameCount != Time.frameCount) {
if (!valid) {
Debug.LogError("Using an invalid GvrControllerInputDevice. Please acquire a new one from GvrControllerInput.GetDevice().");
return;
}
// The controller state must be updated prior to any function using the
// controller API to ensure the state is consistent throughout a frame.
lastUpdatedFrameCount = Time.frameCount;
GvrConnectionState oldState = State;
controllerProvider.ReadState(controllerState, controllerId);
UpdateTouchPosCentered();
#if UNITY_EDITOR
if (IsDominantHand) {
// Make sure the EditorEmulator is updated immediately.
if (GvrEditorEmulator.Instance != null) {
GvrEditorEmulator.Instance.UpdateEditorEmulation();
}
}
#endif // UNITY_EDITOR
if (OnStateChanged != null && State != oldState) {
OnStateChanged(State, oldState);
}
}
}
private void UpdateTouchPosCentered() {
touchPosCentered.x = (controllerState.touchPos.x - 0.5f) * 2.0f;
touchPosCentered.y = -(controllerState.touchPos.y - 0.5f) * 2.0f;
float magnitude = touchPosCentered.magnitude;
if (magnitude > 1) {
touchPosCentered.x /= magnitude;
touchPosCentered.y /= magnitude;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7b912427c85634a3fa13020a8d93c38a
timeCreated: 1519780588
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,163 @@
// 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 System;
using UnityEngine;
using UnityEngine.Assertions;
/// Visualizes a reticle using a Quad.
/// Provides tuning options to control how the reticle scales and rotates based
/// on distance from the camera.
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerReticleVisual")]
public class GvrControllerReticleVisual : MonoBehaviour {
[Serializable]
public struct FaceCameraData {
public bool alongXAxis;
public bool alongYAxis;
public bool alongZAxis;
public bool IsAnyAxisOff {
get {
return !alongXAxis || !alongYAxis || !alongZAxis;
}
}
public FaceCameraData(bool startEnabled) {
alongXAxis = startEnabled;
alongYAxis = startEnabled;
alongZAxis = startEnabled;
}
}
/// If set to false, the scale is simply set to the sizeMeters value.
[Tooltip("Determines if the size of the reticle is based on the distance from the camera.")]
public bool isSizeBasedOnCameraDistance = true;
/// The reticle will be scaled based on the size of the mesh so that it's size matches this size.
[Tooltip("Final size of the reticle in meters when it is 1 meter from the camera.")]
public float sizeMeters = 0.1f;
[Tooltip("Determines if the reticle will always face the camera and along what axes.")]
public FaceCameraData doesReticleFaceCamera = new FaceCameraData(true);
/// Sorting order to use for the reticle's renderer.
/// Range values come from https://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html.
[Range(-32767, 32767)]
public int sortingOrder = 0;
/// The size of the reticle's mesh in meters.
public float ReticleMeshSizeMeters { get; private set; }
/// The ratio of the reticleMeshSizeMeters to 1 meter.
/// If reticleMeshSizeMeters is 10, then reticleMeshSizeRatio is 0.1.
public float ReticleMeshSizeRatio { get; private set; }
protected MeshRenderer meshRenderer;
protected MeshFilter meshFilter;
private Vector3 preRenderLocalScale;
private Quaternion preRenderLocalRotation;
public void RefreshMesh() {
ReticleMeshSizeMeters = 1.0f;
ReticleMeshSizeRatio = 1.0f;
if (meshFilter != null && meshFilter.mesh != null) {
ReticleMeshSizeMeters = meshFilter.mesh.bounds.size.x;
if (ReticleMeshSizeMeters != 0.0f) {
ReticleMeshSizeRatio = 1.0f / ReticleMeshSizeMeters;
}
}
if (meshRenderer != null) {
meshRenderer.sortingOrder = sortingOrder;
}
}
protected virtual void Awake() {
meshRenderer = GetComponent<MeshRenderer>();
meshFilter = GetComponent<MeshFilter>();
}
protected virtual void OnEnable() {
RefreshMesh();
}
protected virtual void OnWillRenderObject() {
preRenderLocalScale = transform.localScale;
preRenderLocalRotation = transform.localRotation;
Camera camera = Camera.current;
UpdateReticleSize(camera);
UpdateReticleOrientation(camera);
}
protected virtual void OnRenderObject() {
// It is possible for paired calls to OnWillRenderObject/OnRenderObject to be nested if
// Camera.Render is explicitly called for any special effects. To avoid the reticle being
// rotated/scaled incorrectly in that case, the reticle is reset to it's pre-OnWillRenderObject
// after a render has finished.
transform.localScale = preRenderLocalScale;
transform.localRotation = preRenderLocalRotation;
}
protected virtual void UpdateReticleSize(Camera camera) {
if (camera == null) {
return;
}
float scale = sizeMeters;
if (isSizeBasedOnCameraDistance) {
float reticleDistanceFromCamera = (transform.position - camera.transform.position).magnitude;
scale *= ReticleMeshSizeRatio * reticleDistanceFromCamera;
}
transform.localScale = new Vector3(scale, scale, scale);
}
protected virtual void UpdateReticleOrientation(Camera camera) {
if (camera == null) {
return;
}
Vector3 direction = transform.position - camera.transform.position;
transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
if (doesReticleFaceCamera.IsAnyAxisOff) {
Vector3 euler = transform.localEulerAngles;
if (!doesReticleFaceCamera.alongXAxis) {
euler.x = 0.0f;
}
if (!doesReticleFaceCamera.alongYAxis) {
euler.y = 0.0f;
}
if (!doesReticleFaceCamera.alongZAxis) {
euler.z = 0.0f;
}
transform.localEulerAngles = euler;
}
}
protected virtual void OnValidate() {
if (Application.isPlaying && isActiveAndEnabled) {
RefreshMesh();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b5cd7f7fcfc8a4c1fbc201ccc579556d
timeCreated: 1495649159
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,347 @@
// Copyright 2016 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.
// The controller is not available for versions of Unity without the
// GVR native integration.
using UnityEngine;
using System.Collections;
/// Provides visual feedback for the daydream controller.
[RequireComponent(typeof(Renderer))]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerVisual")]
public class GvrControllerVisual : MonoBehaviour, IGvrArmModelReceiver, IGvrControllerInputDeviceReceiver {
[System.Serializable]
public struct ControllerDisplayState {
public GvrControllerBatteryLevel batteryLevel;
public bool batteryCharging;
public bool clickButton;
public bool appButton;
public bool homeButton;
public bool touching;
public Vector2 touchPos;
}
/// An array of prefabs that will be instantiated and added as children
/// of the controller visual when the controller is created. Used to
/// attach tooltips or other additional visual elements to the control dynamically.
[SerializeField]
private GameObject[] attachmentPrefabs;
[SerializeField] private Color touchPadColor =
new Color(200f / 255f, 200f / 255f, 200f / 255f, 1);
[SerializeField] private Color appButtonColor =
new Color(200f / 255f, 200f / 255f, 200f / 255f, 1);
[SerializeField] private Color systemButtonColor =
new Color(20f / 255f, 20f / 255f, 20f / 255f, 1);
/// Determines if the displayState is set from GvrControllerInputDevice.
[Tooltip("Determines if the displayState is set from GvrControllerInputDevice.")]
public bool readControllerState = true;
/// Used to set the display state of the controller visual.
/// This can be used for tutorials that visualize the controller or other use-cases that require
/// displaying the controller visual without the state being determined by controller input.
/// Additionally, it can be used to preview the controller visual in the editor.
/// NOTE: readControllerState must be disabled to set the display state.
public ControllerDisplayState displayState;
/// This is the preferred, maximum alpha value the object should have
/// when it is a comfortable distance from the head.
[Range(0.0f, 1.0f)]
public float maximumAlpha = 1.0f;
public GvrBaseArmModel ArmModel { get; set; }
public GvrControllerInputDevice ControllerInputDevice { get; set; }
public float PreferredAlpha{
get{
return ArmModel != null ? maximumAlpha * ArmModel.PreferredAlpha : maximumAlpha;
}
}
public Color TouchPadColor {
get {
return touchPadColor;
}
set {
touchPadColor = value;
if(materialPropertyBlock != null) {
materialPropertyBlock.SetColor(touchPadId, touchPadColor);
}
}
}
public Color AppButtonColor {
get {
return appButtonColor;
}
set {
appButtonColor = value;
if(materialPropertyBlock != null){
materialPropertyBlock.SetColor(appButtonId, appButtonColor);
}
}
}
public Color SystemButtonColor {
get {
return systemButtonColor;
}
set {
systemButtonColor = value;
if(materialPropertyBlock != null) {
materialPropertyBlock.SetColor(systemButtonId, systemButtonColor);
}
}
}
private Renderer controllerRenderer;
private MaterialPropertyBlock materialPropertyBlock;
private int alphaId;
private int touchId;
private int touchPadId;
private int appButtonId;
private int systemButtonId;
private int batteryColorId;
private bool wasTouching;
private float touchTime;
// Data passed to shader, (xy) touch position, (z) touch duration, (w) battery state.
private Vector4 controllerShaderData;
// Data passed to shader, (x) overall alpha, (y) touchpad click duration,
// (z) app button click duration, (w) system button click duration.
private Vector4 controllerShaderData2;
private Color currentBatteryColor;
// These values control animation times for the controller buttons
public const float APP_BUTTON_ACTIVE_DURATION_SECONDS = 0.111f;
public const float APP_BUTTON_RELEASE_DURATION_SECONDS = 0.0909f;
public const float SYSTEM_BUTTON_ACTIVE_DURATION_SECONDS = 0.111f;
public const float SYSTEM_BUTTON_RELEASE_DURATION_SECONDS = 0.0909f;
public const float TOUCHPAD_CLICK_DURATION_SECONDS = 0.111f;
public const float TOUCHPAD_RELEASE_DURATION_SECONDS = 0.0909f;
public const float TOUCHPAD_CLICK_SCALE_DURATION_SECONDS = 0.075f;
public const float TOUCHPAD_POINT_SCALE_DURATION_SECONDS = 0.15f;
// These values are used by the shader to control battery display
// Only modify these values if you are also modifying the shader.
private const float BATTERY_FULL = 0;
private const float BATTERY_ALMOST_FULL = .125f;
private const float BATTERY_MEDIUM = .225f;
private const float BATTERY_LOW = .325f;
private const float BATTERY_CRITICAL = .425f;
private const float BATTERY_HIDDEN = .525f;
private readonly Color GVR_BATTERY_CRITICAL_COLOR = new Color(1,0,0,1);
private readonly Color GVR_BATTERY_LOW_COLOR = new Color(1,0.6823f,0,1);
private readonly Color GVR_BATTERY_MED_COLOR = new Color(0,1,0.588f,1);
private readonly Color GVR_BATTERY_FULL_COLOR = new Color(0,1,0.588f,1);
// How much time to use as an 'immediate update'.
// Any value large enough to instantly update all visual animations.
private const float IMMEDIATE_UPDATE_TIME = 10f;
void Awake() {
Initialize();
CreateAttachments();
}
void OnEnable() {
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
}
void OnDisable() {
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
}
void OnValidate() {
if (!Application.isPlaying) {
Initialize();
OnVisualUpdate(true);
}
}
private void OnPostControllerInputUpdated() {
OnVisualUpdate();
}
private void CreateAttachments() {
if (attachmentPrefabs == null) {
return;
}
for (int i = 0; i < attachmentPrefabs.Length; i++) {
GameObject prefab = attachmentPrefabs[i];
GameObject attachment = Instantiate(prefab);
attachment.transform.SetParent(transform, false);
}
}
private void Initialize() {
if(controllerRenderer == null) {
controllerRenderer = GetComponent<Renderer>();
}
if(materialPropertyBlock == null) {
materialPropertyBlock = new MaterialPropertyBlock();
}
alphaId = Shader.PropertyToID("_GvrControllerAlpha");
touchId = Shader.PropertyToID("_GvrTouchInfo");
touchPadId = Shader.PropertyToID("_GvrTouchPadColor");
appButtonId = Shader.PropertyToID("_GvrAppButtonColor");
systemButtonId = Shader.PropertyToID("_GvrSystemButtonColor");
batteryColorId = Shader.PropertyToID("_GvrBatteryColor");
materialPropertyBlock.SetColor(appButtonId, appButtonColor);
materialPropertyBlock.SetColor(systemButtonId, systemButtonColor);
materialPropertyBlock.SetColor(touchPadId, touchPadColor);
controllerRenderer.SetPropertyBlock(materialPropertyBlock);
}
private void UpdateControllerState() {
// Return early when the application isn't playing to ensure that the serialized displayState
// is used to preview the controller visual instead of the default GvrControllerInputDevice
// values.
#if UNITY_EDITOR
if (!Application.isPlaying) {
return;
}
#endif
if(ControllerInputDevice != null) {
displayState.batteryLevel = ControllerInputDevice.BatteryLevel;
displayState.batteryCharging = ControllerInputDevice.IsCharging;
displayState.clickButton = ControllerInputDevice.GetButton(GvrControllerButton.TouchPadButton);
displayState.appButton = ControllerInputDevice.GetButton(GvrControllerButton.App);
displayState.homeButton = ControllerInputDevice.GetButton(GvrControllerButton.System);
displayState.touching = ControllerInputDevice.GetButton(GvrControllerButton.TouchPadTouch);
displayState.touchPos = ControllerInputDevice.TouchPos;
}
}
private void OnVisualUpdate(bool updateImmediately = false) {
// Update the visual display based on the controller state
if(readControllerState) {
UpdateControllerState();
}
float deltaTime = Time.deltaTime;
// If flagged to update immediately, set deltaTime to an arbitrarily large value
// This is particularly useful in editor, but also for resetting state quickly
if(updateImmediately) {
deltaTime = IMMEDIATE_UPDATE_TIME;
}
if (displayState.clickButton) {
controllerShaderData2.y = Mathf.Min(1, controllerShaderData2.y + deltaTime / TOUCHPAD_CLICK_DURATION_SECONDS);
} else{
controllerShaderData2.y = Mathf.Max(0, controllerShaderData2.y - deltaTime / TOUCHPAD_RELEASE_DURATION_SECONDS);
}
if (displayState.appButton) {
controllerShaderData2.z = Mathf.Min(1, controllerShaderData2.z + deltaTime / APP_BUTTON_ACTIVE_DURATION_SECONDS);
} else{
controllerShaderData2.z = Mathf.Max(0, controllerShaderData2.z - deltaTime / APP_BUTTON_RELEASE_DURATION_SECONDS);
}
if (displayState.homeButton) {
controllerShaderData2.w = Mathf.Min(1, controllerShaderData2.w + deltaTime / SYSTEM_BUTTON_ACTIVE_DURATION_SECONDS);
} else {
controllerShaderData2.w = Mathf.Max(0, controllerShaderData2.w - deltaTime / SYSTEM_BUTTON_RELEASE_DURATION_SECONDS);
}
// Set the material's alpha to the multiplied preferred alpha.
controllerShaderData2.x = PreferredAlpha;
materialPropertyBlock.SetVector(alphaId, controllerShaderData2);
controllerShaderData.x = displayState.touchPos.x;
controllerShaderData.y = displayState.touchPos.y;
if (displayState.touching || displayState.clickButton) {
if (!wasTouching) {
wasTouching = true;
}
if(touchTime < 1) {
touchTime = Mathf.Min(touchTime + deltaTime / TOUCHPAD_POINT_SCALE_DURATION_SECONDS, 1);
}
} else {
wasTouching = false;
if(touchTime > 0) {
touchTime = Mathf.Max(touchTime - deltaTime / TOUCHPAD_POINT_SCALE_DURATION_SECONDS, 0);
}
}
controllerShaderData.z = touchTime;
UpdateBatteryIndicator();
materialPropertyBlock.SetVector(touchId, controllerShaderData);
materialPropertyBlock.SetColor(batteryColorId, currentBatteryColor);
// Update the renderer
controllerRenderer.SetPropertyBlock(materialPropertyBlock);
}
private void UpdateBatteryIndicator() {
GvrControllerBatteryLevel level = displayState.batteryLevel;
bool charging = displayState.batteryCharging;
switch (level) {
case GvrControllerBatteryLevel.Full:
controllerShaderData.w = BATTERY_FULL;
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
break;
case GvrControllerBatteryLevel.AlmostFull:
controllerShaderData.w = BATTERY_ALMOST_FULL;
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
break;
case GvrControllerBatteryLevel.Medium:
controllerShaderData.w = BATTERY_MEDIUM;
currentBatteryColor = GVR_BATTERY_MED_COLOR;
break;
case GvrControllerBatteryLevel.Low:
controllerShaderData.w = BATTERY_LOW;
currentBatteryColor = GVR_BATTERY_LOW_COLOR;
break;
case GvrControllerBatteryLevel.CriticalLow:
controllerShaderData.w = BATTERY_CRITICAL;
currentBatteryColor = GVR_BATTERY_CRITICAL_COLOR;
break;
default:
controllerShaderData.w = BATTERY_HIDDEN;
currentBatteryColor.a = 0;
break;
}
if (charging) {
controllerShaderData.w = -controllerShaderData.w;
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
}
}
public void SetControllerTexture(Texture newTexture) {
controllerRenderer.material.mainTexture = newTexture;
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: aa952cbcc0eb13d4ca558b6da550ff55
timeCreated: 1472074640
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
// 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.
// The controller is not available for versions of Unity without the
// GVR native integration.
using UnityEngine;
using UnityEngine.EventSystems;
/// Implementation of GvrBasePointer for a laser pointer visual.
/// This script should be attached to the controller object.
/// The laser visual is important to help users locate their cursor
/// when its not directly in their field of view.
[RequireComponent(typeof(GvrLaserVisual))]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrLaserPointer")]
public class GvrLaserPointer : GvrBasePointer {
[Tooltip("Distance from the pointer that raycast hits will be detected.")]
public float maxPointerDistance = 20.0f;
[Tooltip("Distance from the pointer that the reticle will be drawn at when hitting nothing.")]
public float defaultReticleDistance = 20.0f;
[Tooltip("By default, the length of the laser is used as the CameraRayIntersectionDistance. " +
"Set this field to a non-zero value to override it.")]
public float overrideCameraRayIntersectionDistance;
/// The percentage of the reticle mesh that shows the reticle.
/// The rest of the reticle mesh is transparent.
private const float RETICLE_VISUAL_RATIO = 0.1f;
public GvrLaserVisual LaserVisual { get; private set; }
private bool isHittingTarget;
public override float MaxPointerDistance {
get {
return maxPointerDistance;
}
}
public override float CameraRayIntersectionDistance {
get {
if (overrideCameraRayIntersectionDistance != 0.0f) {
return overrideCameraRayIntersectionDistance;
}
return LaserVisual != null ?
LaserVisual.maxLaserDistance : overrideCameraRayIntersectionDistance;
}
}
public override void OnPointerEnter(RaycastResult raycastResult, bool isInteractive) {
LaserVisual.SetDistance(raycastResult.distance);
isHittingTarget = true;
}
public override void OnPointerHover(RaycastResult raycastResult, bool isInteractive) {
LaserVisual.SetDistance(raycastResult.distance);
isHittingTarget = true;
}
public override void OnPointerExit(GameObject previousObject) {
// Don't set the distance immediately.
// If we exit/enter an object on the same frame, then SetDistance
// will be called twice which could cause an issue with lerping the reticle.
// If we don't re-enter a new object, the distance will be set in Update.
isHittingTarget = false;
}
public override void OnPointerClickDown() {
}
public override void OnPointerClickUp() {
}
public override void GetPointerRadius(out float enterRadius, out float exitRadius) {
if (LaserVisual.reticle != null) {
float reticleScale = LaserVisual.reticle.transform.localScale.x;
// Fixed size for enter radius to avoid flickering.
// This will cause some slight variability based on the distance of the object
// from the camera, and is optimized for the average case.
enterRadius = LaserVisual.reticle.sizeMeters * 0.5f * RETICLE_VISUAL_RATIO;
// Dynamic size for exit radius.
// Always correct because we know the intersection point of the object and can
// therefore use the correct radius based on the object's distance from the camera.
exitRadius = reticleScale * LaserVisual.reticle.ReticleMeshSizeMeters * RETICLE_VISUAL_RATIO;
} else {
enterRadius = 0.0f;
exitRadius = 0.0f;
}
}
void Awake() {
LaserVisual = GetComponent<GvrLaserVisual>();
}
protected override void Start() {
base.Start();
LaserVisual.GetPointForDistanceFunction = GetPointAlongPointer;
LaserVisual.SetDistance(defaultReticleDistance, true);
}
void Update() {
if (isHittingTarget) {
return;
}
LaserVisual.SetDistance(defaultReticleDistance);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 51b65678ccdd949e9a58874d2880c0ef
timeCreated: 1472486489
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,240 @@
// 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 System;
using UnityEngine;
using UnityEngine.Assertions;
/// Visualizes a laser and a reticle using a LineRenderer and a Quad.
/// Provides functions for settings the end point of the laser,
/// and clamps the laser and reticle based on max distances.
[RequireComponent(typeof(LineRenderer))]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrLaserVisual")]
public class GvrLaserVisual : MonoBehaviour, IGvrArmModelReceiver {
/// Used to position the reticle at the current position.
[Tooltip("Used to position the reticle at the current position.")]
public GvrControllerReticleVisual reticle;
/// The end point of the visual will not necessarily be along the forward direction of the laser.
/// This is particularly true in both Camera and Hybrid Raycast Modes. In that case, both the
/// laser and the controller are rotated to face the end point. This reference is used to control
/// the rotation of the controller.
[Tooltip("Used to rotate the controller to face the current position.")]
public Transform controller;
/// Color of the laser pointer including alpha transparency.
[Tooltip("Start color of the laser pointer including alpha transparency.")]
public Color laserColor = new Color(1.0f, 1.0f, 1.0f, 0.25f);
/// Color of the laser pointer including alpha transparency.
[Tooltip("End color of the laser pointer including alpha transparency.")]
public Color laserColorEnd = new Color(1.0f, 1.0f, 1.0f, 0.0f);
/// Maximum distance of the laser (meters).
[Tooltip("Maximum distance of the laser (meters).")]
[Range(0.0f, 20.0f)]
public float maxLaserDistance = 1.0f;
/// The rate that the current position moves towards the target position.
[Tooltip("The rate that the current position moves towards the target position.")]
public float lerpSpeed = 20.0f;
/// If the targetPosition is greater than this threshold, then
/// the position changes immediately instead of lerping.
[Tooltip("If the target position is greater than this threshold, then the position changes " +
"immediately instead of lerping.")]
public float lerpThreshold = 1.0f;
/// This is primarily used for Hybrid Raycast mode (details in _GvrBasePointer_) to prevent
/// mismatches between the laser and the reticle when the "camera" component of the ray is used.
[Tooltip("Determines if the laser will shrink when it isn't facing in the forward direction " +
"of the transform.")]
public bool shrinkLaser = true;
/// Amount to shrink the laser when it is fully shrunk.
[Range(0.0f, 1.0f)]
[Tooltip("Amount to shrink the laser when it is fully shrunk.")]
public float shrunkScale = 0.2f;
/// Begin shrinking the laser when the angle between transform.forward and the reticle
/// is greater than this value.
[Range(0.0f, 15.0f)]
[Tooltip("Begin shrinking the laser when the angle between transform.forward and the reticle " +
"is greater than this value.")]
public float beginShrinkAngleDegrees = 0.0f;
/// Finish shrinking the laser when the angle between transform.forward and the reticle is
/// greater than this value.
[Range(0.0f, 15.0f)]
[Tooltip("Finish shrinking the laser when the angle between transform.forward and the reticle " +
"is greater than this value.")]
public float endShrinkAngleDegrees = 2.0f;
private const float LERP_CLAMP_THRESHOLD = 0.02f;
public GvrBaseArmModel ArmModel { get; set; }
/// Reference to the laser's line renderer.
public LineRenderer Laser { get; private set; }
/// Optional delegate for customizing how the currentPosition is calculated based on the distance.
/// If not set, the currentPosition is determined based on the distance multiplied by the forward
/// direction of the transform added to the position of the transform.
public delegate Vector3 GetPointForDistanceDelegate(float distance);
public GetPointForDistanceDelegate GetPointForDistanceFunction { get; set; }
protected float shrinkRatio;
protected float targetDistance;
protected float currentDistance;
protected Vector3 currentPosition;
protected Vector3 currentLocalPosition;
protected Quaternion currentLocalRotation;
/// Set the distance of the laser.
/// Clamps the distance of the laser and reticle.
///
/// **distance** target distance from the pointer to draw the visual at.
/// **immediate** If true, the distance is changed immediately. Otherwise, it will lerp.
public virtual void SetDistance(float distance, bool immediate = false) {
targetDistance = distance;
if (immediate) {
currentDistance = targetDistance;
}
if (targetDistance > lerpThreshold) {
currentDistance = targetDistance;
}
}
public float CurrentDistance {
get { return currentDistance; }
}
protected virtual void Awake() {
Laser = GetComponent<LineRenderer>();
}
protected virtual void LateUpdate() {
UpdateCurrentPosition();
UpdateControllerOrientation();
UpdateReticlePosition();
UpdateLaserEndPoint();
UpdateLaserAlpha();
}
protected virtual void UpdateCurrentPosition() {
if (currentDistance != targetDistance) {
float speed = GetSpeed();
currentDistance = Mathf.Lerp(currentDistance, targetDistance, speed);
float diff = Mathf.Abs(targetDistance - currentDistance);
if (diff < LERP_CLAMP_THRESHOLD) {
currentDistance = targetDistance;
}
}
if (GetPointForDistanceFunction != null) {
currentPosition = GetPointForDistanceFunction(currentDistance);
} else {
Vector3 origin = transform.position;
currentPosition = origin + (transform.forward * currentDistance);
}
currentLocalPosition = transform.InverseTransformPoint(currentPosition);
currentLocalRotation = Quaternion.FromToRotation(Vector3.forward, currentLocalPosition);
}
protected virtual void UpdateControllerOrientation() {
if (controller == null) {
return;
}
controller.localRotation = currentLocalRotation;
}
protected virtual void UpdateReticlePosition() {
if (reticle == null) {
return;
}
reticle.transform.position = currentPosition;
}
protected virtual void UpdateLaserEndPoint() {
if (Laser == null) {
return;
}
Vector3 laserStartPoint = Vector3.zero;
Vector3 laserEndPoint;
if (controller != null) {
Vector3 worldPosition = transform.position;
Vector3 rotatedPosition = controller.InverseTransformPoint(worldPosition);
rotatedPosition = currentLocalRotation * rotatedPosition;
laserStartPoint = controller.TransformPoint(rotatedPosition);
laserStartPoint = transform.InverseTransformPoint(laserStartPoint);
}
laserEndPoint = Vector3.ClampMagnitude(currentLocalPosition, maxLaserDistance);
if (shrinkLaser) {
// Calculate the angle of rotation in degrees.
float angle = Vector3.Angle(Vector3.forward, currentLocalPosition);
// Calculate the shrink ratio based on the angle.
float shrinkAngleDelta = endShrinkAngleDegrees - beginShrinkAngleDegrees;
float clampedAngle = Mathf.Clamp(angle - beginShrinkAngleDegrees, 0.0f, shrinkAngleDelta);
shrinkRatio = clampedAngle / shrinkAngleDelta;
// Calculate the shrink coeff.
float shrinkCoeff = GvrMathHelpers.EaseOutCubic(shrunkScale, 1.0f, 1.0f - shrinkRatio);
// Calculate the final distance of the laser.
Vector3 diff = laserStartPoint - currentLocalPosition;
Vector3 dir = diff.normalized;
float dist = Mathf.Min(diff.magnitude, maxLaserDistance) * shrinkCoeff;
// Update the laser start and end points.
laserEndPoint = currentLocalPosition;
laserStartPoint = laserEndPoint + (dir * dist);
}
Laser.useWorldSpace = false;
Laser.SetPosition(0, laserStartPoint);
Laser.SetPosition(1, laserEndPoint);
}
protected virtual void UpdateLaserAlpha() {
float alpha = ArmModel != null ? ArmModel.PreferredAlpha : 1.0f;
Color finalStartColor = Color.Lerp(Color.clear, laserColor, alpha);
Color finalEndColor = laserColorEnd;
// If shrinking the laser, the colors are inversed based on the shrink ratio.
// This is to ensure that the feathering of the laser goes in the right direction.
if (shrinkLaser) {
float colorRatio = GvrMathHelpers.EaseOutCubic(0.0f, 1.0f, shrinkRatio);
finalEndColor = Color.Lerp(finalEndColor, finalStartColor, colorRatio);
finalStartColor = Color.Lerp(finalStartColor, laserColorEnd, colorRatio);
}
Laser.startColor = finalStartColor;
Laser.endColor = finalEndColor;
}
protected virtual float GetSpeed() {
return lerpSpeed > 0.0f ? lerpSpeed * Time.unscaledDeltaTime : 1.0f;
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f4ee199cdc1304b2090f019e4f488674
timeCreated: 1495649159
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
// 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;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#else
using XRSettings = UnityEngine.VR.VRSettings;
#endif // UNITY_2017_2_OR_NEWER
/// Used to recenter only the controllers, required for scenes that have no clear forward direction.
/// Details: https://developers.google.com/vr/distribute/daydream/design-requirements#UX-D6
///
/// Works by offsetting the orientation of the transform when a recenter occurs to correct for the
/// orientation change caused by the recenter event.
///
/// Usage: Place on the parent of the camera that should have it's orientation corrected.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrRecenterOnlyController")]
public class GvrRecenterOnlyController : MonoBehaviour {
private Quaternion lastAppliedYawCorrection = Quaternion.identity;
private Quaternion yawCorrection = Quaternion.identity;
void Update() {
bool connected = false;
foreach (var hand in Gvr.Internal.ControllerUtils.AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
if (device.State == GvrConnectionState.Connected) {
connected = true;
break;
}
}
if (!connected) {
return;
}
// Daydream is loaded only on deivce, not in editor.
#if UNITY_ANDROID && !UNITY_EDITOR
if (XRSettings.loadedDeviceName != GvrSettings.VR_SDK_DAYDREAM) {
return;
}
#endif
if (GvrControllerInput.Recentered) {
ApplyYawCorrection();
return;
}
#if UNITY_EDITOR
// Compatibility for Instant Preview.
if (Gvr.Internal.InstantPreview.Instance != null &&
Gvr.Internal.InstantPreview.Instance.enabled &&
Gvr.Internal.ControllerUtils.AnyButton(GvrControllerButton.System)) {
return;
}
#else // !UNITY_EDITOR
if (Gvr.Internal.ControllerUtils.AnyButton(GvrControllerButton.System)) {
return;
}
#endif // UNITY_EDITOR
yawCorrection = GetYawCorrection();
}
void OnDisable() {
yawCorrection = Quaternion.identity;
RemoveLastYawCorrection();
}
private void ApplyYawCorrection() {
RemoveLastYawCorrection();
transform.localRotation = transform.localRotation * yawCorrection;
lastAppliedYawCorrection = yawCorrection;
}
private void RemoveLastYawCorrection() {
transform.localRotation =
transform.localRotation * Quaternion.Inverse(lastAppliedYawCorrection);
lastAppliedYawCorrection = Quaternion.identity;
}
private Quaternion GetYawCorrection() {
Quaternion headRotation = GvrVRHelpers.GetHeadRotation();
Vector3 euler = headRotation.eulerAngles;
return lastAppliedYawCorrection * Quaternion.Euler(0.0f, euler.y, 0.0f);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a5d8d09b9e5d2437aa022780a2ce8c83
timeCreated: 1487015053
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,238 @@
// 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.Collections;
/// Represents an object tracked by controller input.
/// Manages the active status of the tracked controller based on controller connection status.
/// Fetches a `GvrControllerInputDevice` for the configured `GvrControllerHand` and propagates
/// the device instance to all `IGvrControllerInputDeviceReceiver`s underneath this object on
/// Start and if the controller handedness changes. If the controller is not positionally
/// tracked, position of the object is updated to approximate arm mechanics by using a
/// `GvrBaseArmModel`. `GvrBaseArmModel`s are also propagated to all `IGvrArmModelReceiver`s
/// underneath this object.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrTrackedController")]
public class GvrTrackedController : MonoBehaviour {
[SerializeField]
[Tooltip("Arm model used to control the pose (position and rotation) of the object, " +
"and to propagate to children that implement IGvrArmModelReceiver.")]
private GvrBaseArmModel armModel;
private GvrControllerInputDevice controllerInputDevice;
[SerializeField]
[Tooltip("Is the object's active status determined by the controller connection status.")]
private bool isDeactivatedWhenDisconnected = true;
[SerializeField]
[Tooltip("Controller Hand")]
private GvrControllerHand controllerHand = GvrControllerHand.Dominant;
public GvrControllerInputDevice ControllerInputDevice {
get {
return controllerInputDevice;
}
}
public GvrControllerHand ControllerHand {
get {
return controllerHand;
}
set {
if (value != controllerHand) {
controllerHand = value;
SetupControllerInputDevice();
}
}
}
/// Arm model used to control the pose (position and rotation) of the object, and to propagate to
/// children that implement IGvrArmModelReceiver.
public GvrBaseArmModel ArmModel {
get {
return armModel;
}
set {
if (armModel == value) {
return;
}
armModel = value;
PropagateControllerInputDeviceToArmModel();
PropagateArmModel();
}
}
/// Is the object's active status determined by the controller connection status.
public bool IsDeactivatedWhenDisconnected {
get {
return isDeactivatedWhenDisconnected;
}
set {
if (isDeactivatedWhenDisconnected == value) {
return;
}
isDeactivatedWhenDisconnected = value;
if (isDeactivatedWhenDisconnected) {
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
}
public void PropagateArmModel() {
IGvrArmModelReceiver[] receivers =
GetComponentsInChildren<IGvrArmModelReceiver>(true);
for (int i = 0; i < receivers.Length; i++) {
IGvrArmModelReceiver receiver = receivers[i];
receiver.ArmModel = armModel;
}
}
void Awake() {
// Adding this event handler calls it immediately.
GvrControllerInput.OnDevicesChanged += SetupControllerInputDevice;
}
void OnEnable() {
// Print an error to console if no GvrControllerInput is found.
if (controllerInputDevice.State == GvrConnectionState.Error) {
Debug.LogWarning(controllerInputDevice.ErrorDetails);
}
// Update the position using OnPostControllerInputUpdated.
// This way, the position and rotation will be correct for the entire frame
// so that it doesn't matter what order Updates get called in.
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
/// Force the pose to update immediately in case the controller isn't updated before the next
/// time a frame is rendered.
UpdatePose();
/// Check the controller state immediately whenever this script is enabled.
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
void OnDisable() {
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
}
void Start() {
PropagateArmModel();
if (controllerInputDevice != null) {
PropagateControllerInputDevice();
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
void OnDestroy() {
GvrControllerInput.OnDevicesChanged -= SetupControllerInputDevice;
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
controllerInputDevice = null;
PropagateControllerInputDevice();
}
}
private void PropagateControllerInputDevice() {
IGvrControllerInputDeviceReceiver[] receivers =
GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
foreach (var receiver in receivers) {
receiver.ControllerInputDevice = controllerInputDevice;
}
PropagateControllerInputDeviceToArmModel();
}
private void PropagateControllerInputDeviceToArmModel() {
// Propagate the controller input device to everything in the arm model's object's
// hierarchy in case it is not a child of the tracked controller.
if (armModel != null) {
IGvrControllerInputDeviceReceiver[] receivers =
armModel.GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
foreach (var receiver in receivers) {
receiver.ControllerInputDevice = controllerInputDevice;
}
}
}
private void SetupControllerInputDevice() {
GvrControllerInputDevice newDevice = GvrControllerInput.GetDevice(controllerHand);
if (controllerInputDevice == newDevice) {
return;
}
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
controllerInputDevice = null;
}
controllerInputDevice = newDevice;
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged += OnControllerStateChanged;
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
} else {
OnControllerStateChanged(GvrConnectionState.Disconnected, GvrConnectionState.Disconnected);
}
PropagateControllerInputDevice();
}
private void OnPostControllerInputUpdated() {
UpdatePose();
}
private void OnControllerStateChanged(GvrConnectionState state, GvrConnectionState oldState) {
if (isDeactivatedWhenDisconnected && enabled) {
gameObject.SetActive(state == GvrConnectionState.Connected);
}
}
private void UpdatePose() {
if (controllerInputDevice == null) {
return;
}
// Non-positionally tracked controllers always return Position of Vector3.zero.
if (controllerInputDevice.Position != Vector3.zero) {
transform.localPosition = controllerInputDevice.Position;
transform.localRotation = controllerInputDevice.Orientation;
} else {
if (armModel == null || !controllerInputDevice.IsDominantHand) {
return;
}
transform.localPosition = ArmModel.ControllerPositionFromHead;
transform.localRotation = ArmModel.ControllerRotationFromHead;
}
}
#if UNITY_EDITOR
/// If the "armModel" serialized field is changed while the application is playing
/// by using the inspector in the editor, then we need to call the PropagateArmModel
/// to ensure all children IGvrArmModelReceiver are updated.
/// Outside of the editor, this can't happen because the arm model can only change when
/// a Setter is called that automatically calls PropagateArmModel.
void OnValidate() {
if (Application.isPlaying && isActiveAndEnabled) {
PropagateArmModel();
if (controllerInputDevice != null) {
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
}
#endif // UNITY_EDITOR
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 311793381eb9d45149dc1a422000a9fd
timeCreated: 1481762795
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
// Copyright 2018 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 System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IGvrControllerInputDeviceReceiver {
GvrControllerInputDevice ControllerInputDevice { set; }
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2a4fdc21aee934d82baa564e797da921
timeCreated: 1519951901
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e8d1351fa06562347ad7429db4b16b7c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,43 @@
// Copyright 2016 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;
/// @cond
namespace Gvr.Internal {
/// Factory that provides a concrete implementation of IControllerProvider for the
/// current platform.
static class ControllerProviderFactory {
/// Provides a concrete implementation of IControllerProvider appropriate for the current
/// platform. This method never returns null. In the worst case, it might return a dummy
/// provider if the platform is not supported. For demo purposes the emulator controller
/// is returned in the editor and in Standalone buids, for use inside the desktop player.
static internal IControllerProvider CreateControllerProvider(GvrControllerInput owner) {
// Use emualtor in editor, and in Standalone builds (for demo purposes).
#if UNITY_EDITOR
// Use the Editor controller provider that supports the controller emulator and the mouse.
return new EditorControllerProvider(owner.emulatorConnectionMode);
#elif UNITY_ANDROID
// Use the GVR C API.
return new AndroidNativeControllerProvider();
#else
// Platform not supported.
Debug.LogWarning("No controller support on this platform.");
return new DummyControllerProvider();
#endif // UNITY_EDITOR || UNITY_ANDROID
}
}
}
/// @endcond

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9f9210011b71142d5966eec2db6cc696
timeCreated: 1462043669
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8adfc793bee33b24b861e195c2b5b483
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,459 @@
// 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.
// This provider is only available on an Android device.
#if UNITY_ANDROID && !UNITY_EDITOR
using UnityEngine;
using System;
using System.Runtime.InteropServices;
/// @cond
namespace Gvr.Internal {
/// Controller Provider that uses the native GVR C API to communicate with controllers
/// via Google VR Services on Android.
class AndroidNativeControllerProvider : IControllerProvider {
// Note: keep structs and function signatures in sync with the C header file (gvr_controller.h).
// GVR controller option flags.
private const int GVR_CONTROLLER_ENABLE_ORIENTATION = 1 << 0;
private const int GVR_CONTROLLER_ENABLE_TOUCH = 1 << 1;
private const int GVR_CONTROLLER_ENABLE_GYRO = 1 << 2;
private const int GVR_CONTROLLER_ENABLE_ACCEL = 1 << 3;
private const int GVR_CONTROLLER_ENABLE_GESTURES = 1 << 4;
private const int GVR_CONTROLLER_ENABLE_POSE_PREDICTION = 1 << 5;
private const int GVR_CONTROLLER_ENABLE_POSITION = 1 << 6;
private const int GVR_CONTROLLER_ENABLE_BATTERY = 1 << 7;
private const int GVR_CONTROLLER_ENABLE_ARM_MODEL = 1 << 8;
// enum gvr_controller_button:
private const int GVR_CONTROLLER_BUTTON_NONE = 0;
private const int GVR_CONTROLLER_BUTTON_CLICK = 1;
private const int GVR_CONTROLLER_BUTTON_HOME = 2;
private const int GVR_CONTROLLER_BUTTON_APP = 3;
private const int GVR_CONTROLLER_BUTTON_VOLUME_UP = 4;
private const int GVR_CONTROLLER_BUTTON_VOLUME_DOWN = 5;
private const int GVR_CONTROLLER_BUTTON_RESERVED0 = 6;
private const int GVR_CONTROLLER_BUTTON_RESERVED1 = 7;
private const int GVR_CONTROLLER_BUTTON_RESERVED2 = 8;
private const int GVR_CONTROLLER_BUTTON_COUNT = 9;
// enum gvr_controller_connection_state:
private const int GVR_CONTROLLER_DISCONNECTED = 0;
private const int GVR_CONTROLLER_SCANNING = 1;
private const int GVR_CONTROLLER_CONNECTING = 2;
private const int GVR_CONTROLLER_CONNECTED = 3;
// enum gvr_controller_api_status
private const int GVR_CONTROLLER_API_OK = 0;
private const int GVR_CONTROLLER_API_UNSUPPORTED = 1;
private const int GVR_CONTROLLER_API_NOT_AUTHORIZED = 2;
private const int GVR_CONTROLLER_API_UNAVAILABLE = 3;
private const int GVR_CONTROLLER_API_SERVICE_OBSOLETE = 4;
private const int GVR_CONTROLLER_API_CLIENT_OBSOLETE = 5;
private const int GVR_CONTROLLER_API_MALFUNCTION = 6;
// The serialization of button-state used to determine which buttons are being pressed.
private readonly GvrControllerButton[] GVR_UNITY_BUTTONS = new GvrControllerButton[] {
GvrControllerButton.App,
GvrControllerButton.System,
GvrControllerButton.TouchPadButton,
GvrControllerButton.Reserved0,
GvrControllerButton.Reserved1,
GvrControllerButton.Reserved2
};
private readonly int[] GVR_BUTTONS = new int[] {
GVR_CONTROLLER_BUTTON_APP,
GVR_CONTROLLER_BUTTON_HOME,
GVR_CONTROLLER_BUTTON_CLICK,
GVR_CONTROLLER_BUTTON_RESERVED0,
GVR_CONTROLLER_BUTTON_RESERVED1,
GVR_CONTROLLER_BUTTON_RESERVED2
};
[StructLayout(LayoutKind.Sequential)]
private struct gvr_quat {
internal float x;
internal float y;
internal float z;
internal float w;
}
[StructLayout(LayoutKind.Sequential)]
private struct gvr_vec3 {
internal float x;
internal float y;
internal float z;
}
[StructLayout(LayoutKind.Sequential)]
private struct gvr_vec2 {
internal float x;
internal float y;
}
private const string dllName = GvrActivityHelper.GVR_DLL_NAME;
[DllImport(dllName)]
private static extern int gvr_controller_get_default_options();
[DllImport(dllName)]
private static extern IntPtr gvr_controller_create_and_init_android(
IntPtr jniEnv, IntPtr androidContext, IntPtr classLoader,
int options, IntPtr context);
[DllImport(dllName)]
private static extern void gvr_controller_destroy(ref IntPtr api);
[DllImport(dllName)]
private static extern void gvr_controller_pause(IntPtr api);
[DllImport(dllName)]
private static extern void gvr_controller_resume(IntPtr api);
[DllImport(dllName)]
private static extern IntPtr gvr_controller_state_create();
[DllImport(dllName)]
private static extern void gvr_controller_state_destroy(ref IntPtr state);
[DllImport(dllName)]
private static extern void gvr_controller_state_update(IntPtr api, int flags, IntPtr out_state);
[DllImport(dllName)]
private static extern int gvr_controller_state_get_api_status(IntPtr state);
[DllImport(dllName)]
private static extern int gvr_controller_state_get_connection_state(IntPtr state);
[DllImport(dllName)]
private static extern gvr_quat gvr_controller_state_get_orientation(IntPtr state);
[DllImport(dllName)]
private static extern gvr_vec3 gvr_controller_state_get_position(IntPtr state);
[DllImport(dllName)]
private static extern gvr_vec3 gvr_controller_state_get_gyro(IntPtr state);
[DllImport(dllName)]
private static extern gvr_vec3 gvr_controller_state_get_accel(IntPtr state);
[DllImport(dllName)]
private static extern byte gvr_controller_state_is_touching(IntPtr state);
[DllImport(dllName)]
private static extern gvr_vec2 gvr_controller_state_get_touch_pos(IntPtr state);
[DllImport(dllName)]
private static extern byte gvr_controller_state_get_touch_down(IntPtr state);
[DllImport(dllName)]
private static extern byte gvr_controller_state_get_touch_up(IntPtr state);
[DllImport(dllName)]
private static extern byte gvr_controller_state_get_recentered(IntPtr state);
[DllImport(dllName)]
private static extern byte gvr_controller_state_get_button_state(IntPtr state, int button);
[DllImport(dllName)]
private static extern byte gvr_controller_state_get_button_down(IntPtr state, int button);
[DllImport(dllName)]
private static extern byte gvr_controller_state_get_button_up(IntPtr state, int button);
[DllImport(dllName)]
private static extern long gvr_controller_state_get_last_orientation_timestamp(IntPtr state);
[DllImport(dllName)]
private static extern long gvr_controller_state_get_last_gyro_timestamp(IntPtr state);
[DllImport(dllName)]
private static extern long gvr_controller_state_get_last_accel_timestamp(IntPtr state);
[DllImport(dllName)]
private static extern long gvr_controller_state_get_last_touch_timestamp(IntPtr state);
[DllImport(dllName)]
private static extern long gvr_controller_state_get_last_button_timestamp(IntPtr state);
[DllImport(dllName)]
private static extern byte gvr_controller_state_get_battery_charging(IntPtr state);
[DllImport(dllName)]
private static extern int gvr_controller_state_get_battery_level(IntPtr state);
[DllImport(dllName)]
private static extern long gvr_controller_state_get_last_battery_timestamp(IntPtr state);
[DllImport(dllName)]
private static extern int gvr_controller_get_count(IntPtr api);
private const string VRCORE_UTILS_CLASS = "com.google.vr.vrcore.base.api.VrCoreUtils";
private IntPtr api;
private bool hasBatteryMethods = false;
private AndroidJavaObject androidContext;
private AndroidJavaObject classLoader;
private bool error = false;
private string errorDetails = string.Empty;
private IntPtr statePtr;
private MutablePose3D pose3d = new MutablePose3D();
private GvrControllerButton[] lastButtonsState = new GvrControllerButton[2];
public bool SupportsBatteryStatus {
get { return hasBatteryMethods; }
}
public int MaxControllerCount {
get {
if (api == IntPtr.Zero) {
return 0;
}
return gvr_controller_get_count(api);
}
}
internal AndroidNativeControllerProvider() {
// Debug.Log("Initializing Daydream controller API.");
int options = gvr_controller_get_default_options();
options |= GVR_CONTROLLER_ENABLE_ACCEL;
options |= GVR_CONTROLLER_ENABLE_GYRO;
options |= GVR_CONTROLLER_ENABLE_POSITION;
statePtr = gvr_controller_state_create();
// Get a hold of the activity, context and class loader.
AndroidJavaObject activity = GvrActivityHelper.GetActivity();
if (activity == null) {
error = true;
errorDetails = "Failed to get Activity from Unity Player.";
return;
}
androidContext = GvrActivityHelper.GetApplicationContext(activity);
if (androidContext == null) {
error = true;
errorDetails = "Failed to get Android application context from Activity.";
return;
}
classLoader = GetClassLoaderFromActivity(activity);
if (classLoader == null) {
error = true;
errorDetails = "Failed to get class loader from Activity.";
return;
}
// Use IntPtr instead of GetRawObject() so that Unity can shut down gracefully on
// Application.Quit(). Note that GetRawObject() is not pinned by the receiver so it's not
// cleaned up appropriately on shutdown, which is a known bug in Unity.
IntPtr androidContextPtr = AndroidJNI.NewLocalRef(androidContext.GetRawObject());
IntPtr classLoaderPtr = AndroidJNI.NewLocalRef(classLoader.GetRawObject());
Debug.Log ("Creating and initializing GVR API controller object.");
api = gvr_controller_create_and_init_android (IntPtr.Zero, androidContextPtr, classLoaderPtr,
options, IntPtr.Zero);
AndroidJNI.DeleteLocalRef(androidContextPtr);
AndroidJNI.DeleteLocalRef(classLoaderPtr);
if (IntPtr.Zero == api) {
Debug.LogError("Error creating/initializing Daydream controller API.");
error = true;
errorDetails = "Failed to initialize Daydream controller API.";
return;
}
try {
gvr_controller_state_get_battery_charging(statePtr);
gvr_controller_state_get_battery_level(statePtr);
hasBatteryMethods = true;
} catch (EntryPointNotFoundException) {
// Older VrCore version. Does not support battery indicator.
// Note that controller API is not dynamically loaded as of June 2017 (b/35662043),
// so we'll need to support this case indefinitely...
}
// Debug.Log("GVR API successfully initialized. Now resuming it.");
gvr_controller_resume(api);
// Debug.Log("GVR API resumed.");
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
// Debug.Log("Destroying GVR API structures.");
gvr_controller_state_destroy(ref statePtr);
gvr_controller_destroy(ref api);
if (statePtr != IntPtr.Zero) {
Debug.LogError("gvr_controller_state not zeroed after destroy");
}
if (api != IntPtr.Zero) {
Debug.LogError("gvr_controller_api not zeroed after destroy");
}
// Debug.Log("AndroidNativeControllerProvider destroyed.");
}
}
public void ReadState(ControllerState outState, int controller_id) {
if (error) {
outState.connectionState = GvrConnectionState.Error;
outState.apiStatus = GvrControllerApiStatus.Error;
outState.errorDetails = errorDetails;
return;
}
if (api == IntPtr.Zero || statePtr == IntPtr.Zero) {
Debug.LogError("AndroidNativeControllerProvider used after dispose.");
return;
}
gvr_controller_state_update(api, controller_id, statePtr);
outState.connectionState = ConvertConnectionState(
gvr_controller_state_get_connection_state(statePtr));
outState.apiStatus = ConvertControllerApiStatus(
gvr_controller_state_get_api_status(statePtr));
gvr_quat rawOri = gvr_controller_state_get_orientation(statePtr);
gvr_vec3 rawAccel = gvr_controller_state_get_accel(statePtr);
gvr_vec3 rawGyro = gvr_controller_state_get_gyro(statePtr);
gvr_vec3 rawPos = gvr_controller_state_get_position(statePtr);
// Convert GVR API orientation (right-handed) into Unity axis system (left-handed).
pose3d.Set(new Vector3(rawPos.x,rawPos.y,rawPos.z), new Quaternion(rawOri.x, rawOri.y, rawOri.z, rawOri.w));
pose3d.SetRightHanded(pose3d.Matrix);
outState.orientation = pose3d.Orientation;
outState.position = pose3d.Position;
// For accelerometer, we have to flip Z because the GVR API has Z pointing backwards
// and Unity has Z pointing forward.
outState.accel = new Vector3(rawAccel.x, rawAccel.y, -rawAccel.z);
// Gyro in GVR represents a right-handed angular velocity about each axis (positive means
// clockwise when sighting along axis). Since Unity uses a left-handed system, we flip the
// signs to adjust the sign of the rotational velocity (so that positive means
// counter-clockwise). In addition, since in Unity the Z axis points forward while GVR
// has Z pointing backwards, we flip the Z axis sign again. So the result is that
// we should use -X, -Y, +Z:
outState.gyro = new Vector3(-rawGyro.x, -rawGyro.y, rawGyro.z);
gvr_vec2 touchPos = gvr_controller_state_get_touch_pos(statePtr);
outState.touchPos = new Vector2(touchPos.x, touchPos.y);
outState.buttonsState = 0;
for (int i=0; i<GVR_BUTTONS.Length; i++) {
if (0 != gvr_controller_state_get_button_state(statePtr, GVR_BUTTONS[i])) {
outState.buttonsState |= GVR_UNITY_BUTTONS[i];
}
}
if (0 != gvr_controller_state_is_touching(statePtr)) {
outState.buttonsState |= GvrControllerButton.TouchPadTouch;
}
outState.SetButtonsUpDownFromPrevious(lastButtonsState[controller_id]);
lastButtonsState[controller_id] = outState.buttonsState;
outState.recentered = 0 != gvr_controller_state_get_recentered(statePtr);
outState.gvrPtr = statePtr;
if (hasBatteryMethods) {
outState.isCharging = 0 != gvr_controller_state_get_battery_charging(statePtr);
outState.batteryLevel = (GvrControllerBatteryLevel)gvr_controller_state_get_battery_level(statePtr);
}
}
public void OnPause() {
if (IntPtr.Zero != api) {
gvr_controller_pause(api);
}
}
public void OnResume() {
if (IntPtr.Zero != api) {
gvr_controller_resume(api);
}
}
private GvrConnectionState ConvertConnectionState(int connectionState) {
switch (connectionState) {
case GVR_CONTROLLER_CONNECTED:
return GvrConnectionState.Connected;
case GVR_CONTROLLER_CONNECTING:
return GvrConnectionState.Connecting;
case GVR_CONTROLLER_SCANNING:
return GvrConnectionState.Scanning;
default:
return GvrConnectionState.Disconnected;
}
}
private GvrControllerApiStatus ConvertControllerApiStatus(int gvrControllerApiStatus) {
switch (gvrControllerApiStatus) {
case GVR_CONTROLLER_API_OK:
return GvrControllerApiStatus.Ok;
case GVR_CONTROLLER_API_UNSUPPORTED:
return GvrControllerApiStatus.Unsupported;
case GVR_CONTROLLER_API_NOT_AUTHORIZED:
return GvrControllerApiStatus.NotAuthorized;
case GVR_CONTROLLER_API_SERVICE_OBSOLETE:
return GvrControllerApiStatus.ApiServiceObsolete;
case GVR_CONTROLLER_API_CLIENT_OBSOLETE:
return GvrControllerApiStatus.ApiClientObsolete;
case GVR_CONTROLLER_API_MALFUNCTION:
return GvrControllerApiStatus.ApiMalfunction;
case GVR_CONTROLLER_API_UNAVAILABLE:
default: // Fall through.
return GvrControllerApiStatus.Unavailable;
}
}
private static void UpdateInputEvents(bool currentState, ref bool previousState, ref bool up, ref bool down) {
down = !previousState && currentState;
up = previousState && !currentState;
previousState = currentState;
}
private static AndroidJavaObject GetClassLoaderFromActivity(AndroidJavaObject activity) {
AndroidJavaObject result = activity.Call<AndroidJavaObject>("getClassLoader");
if (result == null) {
Debug.LogErrorFormat("Failed to get class loader from Activity.");
return null;
}
return result;
}
private static int GetVrCoreClientApiVersion(AndroidJavaObject activity) {
try {
AndroidJavaClass utilsClass = new AndroidJavaClass(VRCORE_UTILS_CLASS);
int apiVersion = utilsClass.CallStatic<int>("getVrCoreClientApiVersion", activity);
// Debug.LogFormat("VrCore client API version: " + apiVersion);
return apiVersion;
} catch (Exception exc) {
// Even though a catch-all block is normally frowned upon, in this case we really
// need it because this method has to be robust to unpredictable circumstances:
// VrCore might not exist in the device, the Java layer might be broken, etc, etc.
// None of those should abort the app.
Debug.LogError("Error obtaining VrCore client API version: " + exc);
return 0;
}
}
}
}
/// @endcond
#endif // UNITY_ANDROID && !UNITY_EDITOR

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3fda152dc25154b4a9cccb75fd77f018
timeCreated: 1462060442
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
// Copyright 2016 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 Gvr;
/// @cond
namespace Gvr.Internal {
/// Dummy controller provider.
/// Used in platforms that do not support controllers.
class DummyControllerProvider : IControllerProvider {
private ControllerState dummyState = new ControllerState();
public bool SupportsBatteryStatus {
get { return false; }
}
public int MaxControllerCount {
get { return 1; }
}
internal DummyControllerProvider() {}
public void Dispose() {}
public void ReadState(ControllerState outState,int controller_id) {
outState.CopyFrom(dummyState);
}
public void OnPause() {}
public void OnResume() {}
}
}
/// @endcond

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0f791be37caef48c79f72011276ab16a
timeCreated: 1462043669
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,89 @@
// 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.
// This provider is only available in the editor.
#if UNITY_EDITOR
using Gvr;
namespace Gvr.Internal {
/// Controller provider used when playing in the Unity Editor.
/// Supports the Controller Emulator and Mouse input to mock the controller.
class EditorControllerProvider : IControllerProvider {
private EmulatorControllerProvider emulatorControllerProvider;
private MouseControllerProvider mouseControllerProvider;
#if UNITY_HAS_GOOGLEVR
/// Helper class to get Instant Preview controller events if connected.
private InstantPreviewControllerProvider instantPreviewControllerProvider =
new InstantPreviewControllerProvider();
#endif // UNITY_HAS_GOOGLEVR
ControllerState emulatorState = new ControllerState();
ControllerState mouseState = new ControllerState();
public bool SupportsBatteryStatus {
get { return emulatorControllerProvider.SupportsBatteryStatus; }
}
public int MaxControllerCount {
get { return 1; }
}
internal EditorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
emulatorControllerProvider = new EmulatorControllerProvider(connectionMode);
mouseControllerProvider = new MouseControllerProvider();
}
public void Dispose() {}
public void ReadState(ControllerState outState, int controller_id) {
if (controller_id != 0) {
return;
}
#if UNITY_HAS_GOOGLEVR
if (InstantPreview.Instance != null
&& InstantPreview.Instance.IsCurrentlyConnected
&& !EmulatorManager.Instance.Connected) {
// Uses Instant Preview to get controller state if connected.
instantPreviewControllerProvider.ReadState(outState);
return;
}
#endif // UNITY_HAS_GOOGLEVR
// If Instant Preview is not connected, tries to use the emulator or
// mouse.
emulatorControllerProvider.ReadState(emulatorState, controller_id);
mouseControllerProvider.ReadState(mouseState, controller_id);
// Defaults to mouse state if the emulator isn't available.
if (emulatorState.connectionState != GvrConnectionState.Connected
&& mouseState.connectionState == GvrConnectionState.Connected) {
outState.CopyFrom(mouseState);
} else {
outState.CopyFrom(emulatorState);
}
}
public void OnPause() {
emulatorControllerProvider.OnPause();
mouseControllerProvider.OnPause();
}
public void OnResume() {
emulatorControllerProvider.OnResume();
mouseControllerProvider.OnResume();
}
}
}
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e0a32f2191dec4e1b8e05acab6002be6
timeCreated: 1496354837
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,191 @@
// Copyright 2016 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.
// This class is only used in the Editor, so make sure to only compile it on that platform.
// Additionally, it depends on EmulatorManager which is only compiled in the editor.
#if UNITY_EDITOR
using UnityEngine;
/// @cond
namespace Gvr.Internal {
/// Controller provider that connects to the controller emulator to obtain controller events.
class EmulatorControllerProvider : IControllerProvider {
private ControllerState state = new ControllerState();
/// Yaw correction due to recentering.
private Quaternion yawCorrection = Quaternion.identity;
/// True if we performed the initial recenter.
private bool initialRecenterDone = false;
/// The last (uncorrected) orientation received from the emulator.
private Quaternion lastRawOrientation = Quaternion.identity;
private GvrControllerButton lastButtonsState;
public bool SupportsBatteryStatus {
get { return true; }
}
public int MaxControllerCount {
get { return 1; }
}
/// Creates a new EmulatorControllerProvider with the specified settings.
internal EmulatorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
if (connectionMode == GvrControllerInput.EmulatorConnectionMode.USB) {
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.USB;
} else if (connectionMode == GvrControllerInput.EmulatorConnectionMode.WIFI) {
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.WIFI;
} else {
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.OFF;
}
EmulatorManager.Instance.touchEventListeners += HandleTouchEvent;
EmulatorManager.Instance.orientationEventListeners += HandleOrientationEvent;
EmulatorManager.Instance.buttonEventListeners += HandleButtonEvent;
EmulatorManager.Instance.gyroEventListeners += HandleGyroEvent;
EmulatorManager.Instance.accelEventListeners += HandleAccelEvent;
}
public void Dispose() {}
public void ReadState(ControllerState outState, int controller_id) {
if (controller_id != 0) {
return;
}
lock (state) {
state.connectionState = GvrConnectionState.Connected;
if (!EmulatorManager.Instance.Connected) {
state.connectionState = EmulatorManager.Instance.Connecting ?
GvrConnectionState.Connecting : GvrConnectionState.Disconnected;
}
state.apiStatus = EmulatorManager.Instance.Connected ? GvrControllerApiStatus.Ok :
GvrControllerApiStatus.Unavailable;
// During emulation, just assume the controller is fully charged
state.isCharging = false;
state.batteryLevel = GvrControllerBatteryLevel.Full;
state.SetButtonsUpDownFromPrevious(lastButtonsState);
lastButtonsState = state.buttonsState;
outState.CopyFrom(state);
}
state.ClearTransientState();
}
public void OnPause() {}
public void OnResume() {}
private void HandleTouchEvent(EmulatorTouchEvent touchEvent) {
if (touchEvent.pointers.Count < 1) return;
EmulatorTouchEvent.Pointer pointer = touchEvent.pointers[0];
lock (state) {
state.touchPos = new Vector2(pointer.normalizedX, pointer.normalizedY);
switch (touchEvent.getActionMasked()) {
case EmulatorTouchEvent.Action.kActionDown:
state.buttonsState |= GvrControllerButton.TouchPadTouch;
break;
case EmulatorTouchEvent.Action.kActionMove:
state.buttonsState |= GvrControllerButton.TouchPadTouch;
break;
case EmulatorTouchEvent.Action.kActionUp:
state.buttonsState &= ~GvrControllerButton.TouchPadTouch;
break;
}
}
}
private void HandleOrientationEvent(EmulatorOrientationEvent orientationEvent) {
lastRawOrientation = ConvertEmulatorQuaternion(orientationEvent.orientation);
if (!initialRecenterDone) {
Recenter();
initialRecenterDone = true;
}
lock (state) {
state.orientation = yawCorrection * lastRawOrientation;
}
}
private void HandleButtonEvent(EmulatorButtonEvent buttonEvent) {
GvrControllerButton buttonMask = 0;
switch (buttonEvent.code) {
case EmulatorButtonEvent.ButtonCode.kApp:
buttonMask = GvrControllerButton.App;
break;
case EmulatorButtonEvent.ButtonCode.kHome:
buttonMask = GvrControllerButton.System;
break;
case EmulatorButtonEvent.ButtonCode.kClick:
buttonMask = GvrControllerButton.TouchPadButton;
break;
}
if (buttonMask != 0) {
lock (state) {
state.buttonsState &= ~buttonMask;
if (buttonEvent.down) {
state.buttonsState |= buttonMask;
}
}
if (buttonMask == GvrControllerButton.System) {
if (!buttonEvent.down) {
// Finished the recentering gesture. Recenter controller.
Recenter();
}
}
}
}
private void HandleGyroEvent(EmulatorGyroEvent gyroEvent) {
lock (state) {
state.gyro = ConvertEmulatorGyro(gyroEvent.value);
}
}
private void HandleAccelEvent(EmulatorAccelEvent accelEvent) {
lock (state) {
state.accel = ConvertEmulatorAccel(accelEvent.value);
}
}
private static Quaternion ConvertEmulatorQuaternion(Quaternion emulatorQuat) {
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
return new Quaternion(emulatorQuat.x, -emulatorQuat.z, emulatorQuat.y, emulatorQuat.w);
}
private static Vector3 ConvertEmulatorGyro(Vector3 emulatorGyro) {
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
return new Vector3(-emulatorGyro.x, -emulatorGyro.z, -emulatorGyro.y);
}
private static Vector3 ConvertEmulatorAccel(Vector3 emulatorAccel) {
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
return new Vector3(emulatorAccel.x, emulatorAccel.z, emulatorAccel.y);
}
private void Recenter() {
lock (state) {
// We want the current orientation to be "forward" so, we set the yaw correction
// to undo the current rotation's yaw.
yawCorrection = Quaternion.AngleAxis(-lastRawOrientation.eulerAngles.y, Vector3.up);
state.orientation = Quaternion.identity;
state.recentered = true;
}
}
}
}
/// @endcond
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0ca644865f5f4479fb50471605078cf0
timeCreated: 1462051657
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,198 @@
// 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 Gvr;
using UnityEngine;
namespace Gvr.Internal {
/// Mocks controller input by using the mouse.
/// The controller is connected when holding left shift.
/// Move the mouse to control gyroscope and orientation.
/// The left mouse button is used for the clickButton.
/// The right mouse button is used for the appButton.
/// The middle mouse button is used for the homeButton.
class MouseControllerProvider : IControllerProvider {
private const string AXIS_MOUSE_X = "Mouse X";
private const string AXIS_MOUSE_Y = "Mouse Y";
private ControllerState state = new ControllerState();
private Vector2 mouseDelta = new Vector2();
/// Need to store the state of the buttons from the previous frame.
/// This is because Input.GetMouseButtonDown and Input.GetMouseButtonUp
/// don't work when called after WaitForEndOfFrame, which is when ReadState is called.
private bool wasTouching;
private GvrControllerButton lastButtonsState;
private const float ROTATE_SENSITIVITY = 4.5f;
private const float TOUCH_SENSITIVITY = .12f;
private static readonly Vector3 INVERT_Y = new Vector3(1, -1, 1);
public static bool IsMouseAvailable {
get {
return Input.mousePresent && IsActivateButtonPressed;
}
}
public static bool IsActivateButtonPressed {
get {
return Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
}
}
public static bool IsClickButtonPressed {
get {
return Input.GetMouseButton(0);
}
}
public static bool IsAppButtonPressed {
get {
return Input.GetMouseButton(1);
}
}
public static bool IsHomeButtonPressed {
get {
return Input.GetMouseButton(2);
}
}
public static bool IsTouching {
get {
return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
}
}
public bool SupportsBatteryStatus {
get { return false; }
}
public int MaxControllerCount {
get { return 1; }
}
internal MouseControllerProvider() {}
public void Dispose() {}
public void ReadState(ControllerState outState, int controller_id) {
if (controller_id != 0) {
return;
}
lock (state) {
UpdateState();
outState.CopyFrom(state);
}
state.ClearTransientState();
}
public void OnPause() {}
public void OnResume() {}
private void UpdateState() {
GvrCursorHelper.ControllerEmulationActive = IsMouseAvailable;
if (!IsMouseAvailable) {
ClearState();
return;
}
state.connectionState = GvrConnectionState.Connected;
state.apiStatus = GvrControllerApiStatus.Ok;
state.isCharging = false;
state.batteryLevel = GvrControllerBatteryLevel.Full;
UpdateButtonStates();
mouseDelta.Set(
Input.GetAxis(AXIS_MOUSE_X),
Input.GetAxis(AXIS_MOUSE_Y)
);
if (0 != (state.buttonsState & GvrControllerButton.TouchPadTouch)) {
UpdateTouchPos();
} else {
UpdateOrientation();
}
}
private void UpdateTouchPos() {
Vector3 currentMousePosition = Input.mousePosition;
Vector2 touchDelta = mouseDelta * TOUCH_SENSITIVITY;
touchDelta.y *= -1.0f;
state.touchPos += touchDelta;
state.touchPos.x = Mathf.Clamp01(state.touchPos.x);
state.touchPos.y = Mathf.Clamp01(state.touchPos.y);
}
private void UpdateOrientation() {
Vector3 deltaDegrees = Vector3.Scale(mouseDelta, INVERT_Y) * ROTATE_SENSITIVITY;
state.gyro = deltaDegrees * (Mathf.Deg2Rad / Time.deltaTime);
Quaternion yaw = Quaternion.AngleAxis(deltaDegrees.x, Vector3.up);
Quaternion pitch = Quaternion.AngleAxis(deltaDegrees.y, Vector3.right);
state.orientation = state.orientation * yaw * pitch;
}
private void UpdateButtonStates() {
state.buttonsState = 0;
if (IsClickButtonPressed) {
state.buttonsState |= GvrControllerButton.TouchPadButton;
}
if (IsAppButtonPressed) {
state.buttonsState |= GvrControllerButton.App;
}
if (IsHomeButtonPressed) {
state.buttonsState |= GvrControllerButton.System;
}
if (IsTouching) {
state.buttonsState |= GvrControllerButton.TouchPadTouch;
}
state.SetButtonsUpDownFromPrevious(lastButtonsState);
lastButtonsState = state.buttonsState;
if (0 != (state.buttonsUp & GvrControllerButton.TouchPadTouch)) {
ClearTouchPos();
}
if (0 != (state.buttonsUp & GvrControllerButton.System)) {
Recenter();
}
}
private void Recenter() {
Quaternion yawCorrection = Quaternion.AngleAxis(-state.orientation.eulerAngles.y, Vector3.up);
state.orientation = state.orientation * yawCorrection;
state.recentered = true;
}
private void ClearTouchPos() {
state.touchPos = new Vector2(0.5f, 0.5f);
}
private void ClearState() {
state.connectionState = GvrConnectionState.Disconnected;
state.buttonsState = 0;
state.buttonsDown = 0;
state.buttonsUp = 0;
ClearTouchPos();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d8b9c789363a44b1e87727b1c4d085f6
timeCreated: 1496352344
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
// Copyright 2016 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 Gvr;
/// @cond
namespace Gvr.Internal {
/// Internal representation of the controller's current state.
/// This representation is used by controller providers to represent the controller's state.
///
/// The fields in this class have identical meanings to their correspondents in the GVR C API,
/// so they are not redundantly documented here.
class ControllerState {
internal GvrConnectionState connectionState = GvrConnectionState.Disconnected;
internal GvrControllerApiStatus apiStatus = GvrControllerApiStatus.Unavailable;
internal Quaternion orientation = Quaternion.identity;
internal Vector3 position = Vector3.zero;
internal Vector3 gyro = Vector3.zero;
internal Vector3 accel = Vector3.zero;
internal Vector2 touchPos = Vector2.zero;
internal bool recentered = false;
internal GvrControllerButton buttonsState;
internal GvrControllerButton buttonsDown;
internal GvrControllerButton buttonsUp;
internal string errorDetails = "";
internal IntPtr gvrPtr = IntPtr.Zero;
internal bool isCharging = false;
internal GvrControllerBatteryLevel batteryLevel = GvrControllerBatteryLevel.Unknown;
public void CopyFrom(ControllerState other) {
connectionState = other.connectionState;
apiStatus = other.apiStatus;
orientation = other.orientation;
position = other.position;
gyro = other.gyro;
accel = other.accel;
touchPos = other.touchPos;
recentered = other.recentered;
buttonsState = other.buttonsState;
buttonsDown = other.buttonsDown;
buttonsUp = other.buttonsUp;
errorDetails = other.errorDetails;
gvrPtr = other.gvrPtr;
isCharging = other.isCharging;
batteryLevel = other.batteryLevel;
}
/// Resets the transient state (the state variables that represent events, and which are true
/// for only one frame).
public void ClearTransientState() {
recentered = false;
buttonsState = 0;
buttonsDown = 0;
buttonsUp = 0;
}
public void SetButtonsUpDownFromPrevious(GvrControllerButton prevButtonsState) {
buttonsDown = ~prevButtonsState & buttonsState;
buttonsUp = prevButtonsState & ~buttonsState;
}
}
}
/// @endcond

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9159532a8a3d946aa9df74e771243e5b
timeCreated: 1462043669
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
// Copyright 2018 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 Gvr;
/// @cond
namespace Gvr.Internal {
class ControllerUtils {
/// Convenience array of all hands.
public static GvrControllerHand[] AllHands = {
GvrControllerHand.Right,
GvrControllerHand.Left,
};
/// Returns true while the user holds down any of buttons specified in `buttons` on
/// any controller.
public static bool AnyButton(GvrControllerButton buttons) {
bool ret = false;
foreach (var hand in AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
ret |= device.GetButton(buttons);
}
return ret;
}
/// Returns true in the frame the user starts pressing down any of buttons specified
/// in `buttons` on any controller.
public static bool AnyButtonDown(GvrControllerButton buttons) {
bool ret = false;
foreach (var hand in AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
ret |= device.GetButtonDown(buttons);
}
return ret;
}
/// Returns true the frame after the user stops pressing down any of buttons specified
/// in `buttons` on any controller.
public static bool AnyButtonUp(GvrControllerButton buttons) {
bool ret = false;
foreach (var hand in AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
ret |= device.GetButtonUp(buttons);
}
return ret;
}
}
}
/// @endcond

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8358cf789a848412fbb10ba05c4f3fce
timeCreated: 1524508137
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9c82eb0d7ee68d344bd339cec427a446
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,258 @@
// Copyright 2016 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.
// This class is only used in the Editor, so make sure to only compile it on that platform.
// Additionally, If this class is compiled on Android then Unity will insert the INTERNET permission
// into the manifest because of the reference to the type TCPClient. Excluding this class in the android
// build ensures that it is only included if the developer using the SDK actually uses INTERNET related services.
// This MonoBehaviour is only ever instantiated dynamically, so it is fine that it is only compiled in the Editor,
// Otherwise it would cause serialization issues.
#if UNITY_EDITOR
using UnityEngine;
using System;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using proto;
/// @cond
namespace Gvr.Internal {
public enum EmulatorClientSocketConnectionState {
Disconnected = 0,
Connecting = 1,
Connected = 2,
};
class EmulatorClientSocket : MonoBehaviour {
private static readonly int kPhoneEventPort = 7003;
private const int kSocketReadTimeoutMillis = 5000;
// Minimum interval, in seconds, between attempts to reconnect the socket.
private const float kMinReconnectInterval = 1f;
private TcpClient phoneMirroringSocket;
private Thread phoneEventThread;
private volatile bool shouldStop = false;
// Flag used to limit connection state logging to initial failure and successful reconnects.
private volatile bool lastConnectionAttemptWasSuccessful = true;
private EmulatorManager phoneRemote;
public EmulatorClientSocketConnectionState connected { get; private set; }
public void Init(EmulatorManager remote) {
phoneRemote = remote;
if (EmulatorConfig.Instance.PHONE_EVENT_MODE != EmulatorConfig.Mode.OFF) {
phoneEventThread = new Thread(phoneEventSocketLoop);
phoneEventThread.IsBackground = true;
phoneEventThread.Start();
}
}
private void phoneEventSocketLoop() {
while (!shouldStop) {
long lastConnectionAttemptTime = DateTime.Now.Ticks;
try {
phoneConnect();
} catch(Exception e) {
if (lastConnectionAttemptWasSuccessful) {
Debug.LogWarningFormat("{0}\n{1}", e.Message, e.StackTrace);
// Suppress additional failures until we have successfully reconnected.
lastConnectionAttemptWasSuccessful = false;
}
}
// Wait a while in order to enforce the minimum time between connection attempts.
TimeSpan elapsed = new TimeSpan(DateTime.Now.Ticks - lastConnectionAttemptTime);
float toWait = kMinReconnectInterval - (float) elapsed.TotalSeconds;
if (toWait > 0) {
Thread.Sleep((int) (toWait * 1000));
}
}
}
private void phoneConnect() {
string addr = EmulatorConfig.Instance.PHONE_EVENT_MODE == EmulatorConfig.Mode.USB
? EmulatorConfig.USB_SERVER_IP : EmulatorConfig.WIFI_SERVER_IP;
try {
if (EmulatorConfig.Instance.PHONE_EVENT_MODE == EmulatorConfig.Mode.USB) {
setupPortForwarding(kPhoneEventPort);
}
TcpClient tcpClient = new TcpClient(addr, kPhoneEventPort);
connected = EmulatorClientSocketConnectionState.Connecting;
ProcessConnection(tcpClient);
tcpClient.Close();
} finally {
connected = EmulatorClientSocketConnectionState.Disconnected;
}
}
private void setupPortForwarding(int port) {
#if !UNITY_WEBPLAYER
string adbCommand = string.Format("adb forward tcp:{0} tcp:{0}", port);
System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
string processFilename;
string processArguments;
int kExitCodeCommandNotFound;
if (Application.platform == RuntimePlatform.WindowsEditor ||
Application.platform == RuntimePlatform.WindowsPlayer) {
processFilename = "CMD.exe";
processArguments = @"/k " + adbCommand + " & exit";
// See "Common Error Lookup Tool" (https://www.microsoft.com/en-us/download/details.aspx?id=985)
// MSG_DIR_BAD_COMMAND_OR_FILE (cmdmsg.h)
kExitCodeCommandNotFound = 9009; // 0x2331
} else { // Unix
processFilename = "bash";
processArguments = string.Format("-l -c \"{0}\"", adbCommand);
// "command not found" (see http://tldp.org/LDP/abs/html/exitcodes.html)
kExitCodeCommandNotFound = 127;
}
System.Diagnostics.ProcessStartInfo myProcessStartInfo =
new System.Diagnostics.ProcessStartInfo(processFilename, processArguments);
myProcessStartInfo.UseShellExecute = false;
myProcessStartInfo.RedirectStandardError = true;
myProcessStartInfo.CreateNoWindow = true;
myProcess.StartInfo = myProcessStartInfo;
myProcess.Start();
myProcess.WaitForExit();
// Also wait for HasExited here, to avoid ExitCode access below occasionally throwing InvalidOperationException
while (!myProcess.HasExited) {
Thread.Sleep(1);
}
int exitCode = myProcess.ExitCode;
string standardError = myProcess.StandardError.ReadToEnd();
myProcess.Close();
if (exitCode == 0) {
// Port forwarding setup successfully.
return;
}
if (exitCode == kExitCodeCommandNotFound) {
// Caught by phoneEventSocketLoop.
throw new Exception(
"Android Debug Bridge (`adb`) command not found." +
"\nVerify that the Android SDK is installed and that the directory containing" +
" `adb` is included in your PATH environment variable.");
}
// Caught by phoneEventSocketLoop.
throw new Exception(
string.Format(
"Failed to setup port forwarding." +
" Exit code {0} returned by process: {1} {2}\n{3}",
exitCode, processFilename, processArguments, standardError));
#endif // !UNITY_WEBPLAYER
}
private void ProcessConnection(TcpClient tcpClient) {
byte[] buffer = new byte[4];
NetworkStream stream = tcpClient.GetStream();
stream.ReadTimeout = kSocketReadTimeoutMillis;
tcpClient.ReceiveTimeout = kSocketReadTimeoutMillis;
while (!shouldStop) {
int bytesRead = blockingRead(stream, buffer, 0, 4);
if (bytesRead < 4) {
// Caught by phoneEventSocketLoop.
throw new Exception(
"Failed to read from controller emulator app event socket." +
"\nVerify that the controller emulator app is running.");
}
int msgLen = unpack32bits(correctEndianness(buffer), 0);
byte[] dataBuffer = new byte[msgLen];
bytesRead = blockingRead(stream, dataBuffer, 0, msgLen);
if (bytesRead < msgLen) {
// Caught by phoneEventSocketLoop.
throw new Exception(
"Failed to read from controller emulator app event socket." +
"\nVerify that the controller emulator app is running.");
}
PhoneEvent proto =
PhoneEvent.CreateBuilder().MergeFrom(dataBuffer).Build();
phoneRemote.OnPhoneEvent(proto);
connected = EmulatorClientSocketConnectionState.Connected;
if (!lastConnectionAttemptWasSuccessful) {
Debug.Log("Successfully connected to controller emulator app.");
// Log first failure after after successful read from event socket.
lastConnectionAttemptWasSuccessful = true;
}
}
}
private int blockingRead(NetworkStream stream, byte[] buffer, int index,
int count) {
int bytesRead = 0;
while (!shouldStop && bytesRead < count) {
try {
int n = stream.Read(buffer, index + bytesRead, count - bytesRead);
if (n <= 0) {
// Failed to read.
return -1;
}
bytesRead += n;
} catch (IOException) {
// Read failed or timed out.
return -1;
} catch (ObjectDisposedException) {
// Socket closed.
return -1;
}
}
return bytesRead;
}
void OnDestroy() {
shouldStop = true;
if (phoneMirroringSocket != null) {
phoneMirroringSocket.Close ();
phoneMirroringSocket = null;
}
if (phoneEventThread != null) {
phoneEventThread.Join();
}
}
private int unpack32bits(byte[] array, int offset) {
int num = 0;
for (int i = 0; i < 4; i++) {
num += array [offset + i] << (i * 8);
}
return num;
}
static private byte[] correctEndianness(byte[] array) {
if (BitConverter.IsLittleEndian)
Array.Reverse(array);
return array;
}
}
}
/// @endcond
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 6bf1f92fb4ae24291b71e77c1ccac323
timeCreated: 1462051657
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,60 @@
// Copyright 2016 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;
/// @cond
namespace Gvr.Internal {
class EmulatorConfig : MonoBehaviour {
public static EmulatorConfig Instance {
get {
if (instance == null) {
EmulatorConfig[] configs = (EmulatorConfig[]) FindObjectsOfType(typeof(EmulatorConfig));
if (configs.Length == 1) {
instance = configs[0];
} else if (configs.Length > 1) {
Debug.LogError(
"Multiple PhoneRemote/Config objects in scene. Ignoring all.");
}
}
if (instance == null) {
var gameObject = new GameObject("PhoneRemoteConfig");
instance = gameObject.AddComponent<EmulatorConfig>();
DontDestroyOnLoad(instance);
}
return instance;
}
}
private static EmulatorConfig instance = null;
public enum Mode {
OFF,
USB,
WIFI,
}
// Set this value to match how the PC is connected to the phone that is
// streaming gyro, accel, and touch events. Set to OFF if using Wifi instead.
public Mode PHONE_EVENT_MODE = Mode.USB;
/*----- Internal Parameters (should not require any changes). -----*/
// IP address of the phone, when connected to the PC via USB.
public static readonly string USB_SERVER_IP = "127.0.0.1";
// IP address of the phone, when connected to the PC via WiFi.
public static readonly string WIFI_SERVER_IP = "192.168.43.1";
}
}
/// @endcond

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a229fefd8ee7448b0b700f6000ebdec3
timeCreated: 1462051657
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,189 @@
// Copyright 2016 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 System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using proto;
/// @cond
namespace Gvr.Internal {
struct EmulatorGyroEvent {
public readonly long timestamp;
public readonly Vector3 value;
public EmulatorGyroEvent(PhoneEvent.Types.GyroscopeEvent proto) {
timestamp = proto.Timestamp;
value = new Vector3(proto.X, proto.Y, proto.Z);
}
}
struct EmulatorAccelEvent {
public readonly long timestamp;
public readonly Vector3 value;
public EmulatorAccelEvent(PhoneEvent.Types.AccelerometerEvent proto) {
timestamp = proto.Timestamp;
value = new Vector3(proto.X, proto.Y, proto.Z);
}
}
struct EmulatorTouchEvent {
// Action constants. These should match the constants in the Android
// MotionEvent:
// http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_CANCEL
public enum Action {
kActionDown = 0,
kActionUp = 1,
kActionMove = 2,
kActionCancel = 3,
kActionPointerDown = 5,
kActionPointerUp = 6,
kActionHoverMove = 7,
kActionHoverEnter = 9,
kActionHoverExit = 10
};
// Use getActionMasked() and getActionPointer() instead.
private readonly int action;
public readonly int relativeTimestamp;
public readonly List<Pointer> pointers;
public struct Pointer {
public readonly int fingerId;
public readonly float normalizedX;
public readonly float normalizedY;
public Pointer(int fingerId, float normalizedX, float normalizedY) {
this.fingerId = fingerId;
this.normalizedX = normalizedX;
this.normalizedY = normalizedY;
}
public override string ToString () {
return string.Format ("({0}, {1}, {2})", fingerId, normalizedX,
normalizedY);
}
}
public EmulatorTouchEvent(PhoneEvent.Types.MotionEvent proto, long lastDownTimeMs) {
action = proto.Action;
relativeTimestamp =
(Action)(proto.Action & ACTION_MASK) == Action.kActionDown
? 0 : (int) (proto.Timestamp - lastDownTimeMs);
pointers = new List<Pointer>();
foreach (PhoneEvent.Types.MotionEvent.Types.Pointer pointer in
proto.PointersList) {
pointers.Add(
new Pointer(pointer.Id, pointer.NormalizedX, pointer.NormalizedY));
}
}
public EmulatorTouchEvent(Action action, int pointerId, int relativeTimestamp,
List<Pointer> pointers) {
int fingerIndex = 0;
if (action == Action.kActionPointerDown
|| action == Action.kActionPointerUp) {
fingerIndex = findPointerIndex(pointerId, pointers);
if (fingerIndex == -1) {
Debug.LogWarning("Could not find specific fingerId " + pointerId
+ " in the supplied list of pointers.");
fingerIndex = 0;
}
}
this.action = getActionUnmasked(action, fingerIndex);
this.relativeTimestamp = relativeTimestamp;
this.pointers = pointers;
}
// See Android's getActionMasked() and getActionIndex().
private static readonly int ACTION_POINTER_INDEX_SHIFT = 8;
private static readonly int ACTION_POINTER_INDEX_MASK = 0xff00;
private static readonly int ACTION_MASK = 0xff;
public Action getActionMasked() {
return (Action)(action & ACTION_MASK);
}
public Pointer getActionPointer() {
int index =
(action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
return pointers[index];
}
private static int getActionUnmasked(Action action, int fingerIndex) {
return ((int)action) | (fingerIndex << ACTION_POINTER_INDEX_SHIFT);
}
private static int findPointerIndex(int fingerId, List<Pointer> pointers) {
// Encode the fingerId info into the action, as Android does. See Android's
// getActionMasked() and getActionIndex().
int fingerIndex = -1;
for (int i = 0; i < pointers.Count; i++) {
if (fingerId == pointers[i].fingerId) {
fingerIndex = i;
break;
}
}
return fingerIndex;
}
public override string ToString () {
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
builder.AppendFormat("t = {0}; A = {1}; P = {2}; N = {3}; [",
relativeTimestamp, getActionMasked (), getActionPointer ().fingerId,
pointers.Count);
for (int i = 0; i < pointers.Count; i++) {
builder.Append(pointers[i]).Append (", ");
}
builder.Append ("]");
return builder.ToString();
}
}
struct EmulatorOrientationEvent {
public readonly long timestamp;
public readonly Quaternion orientation;
public EmulatorOrientationEvent(PhoneEvent.Types.OrientationEvent proto) {
timestamp = proto.Timestamp;
// Convert from right-handed coordinates to left-handed.
orientation = new Quaternion(proto.X, proto.Y, -proto.Z, proto.W);
}
}
struct EmulatorButtonEvent {
// Codes as reported by the IC app (reuses Android KeyEvent codes).
public enum ButtonCode {
kNone = 0,
kHome = 3, // android.view.KeyEvent.KEYCODE_HOME
kVolumeUp = 25, // android.view.KeyEvent.KEYCODE_VOLUME_UP
kVolumeDown = 24, // android.view.KeyEvent.KEYCODE_VOLUME_DOWN
kClick = 66, // android.view.KeyEvent.KEYCODE_ENTER
kApp = 82, // android.view.KeyEvent.KEYCODE_MENU
}
public readonly ButtonCode code;
public readonly bool down;
public EmulatorButtonEvent(PhoneEvent.Types.KeyEvent proto) {
code = (ButtonCode) proto.Code;
down = proto.Action == 0;
}
}
}
/// @endcond

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 76d2b695633884daf905c07095c8a01c
timeCreated: 1462051657
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,250 @@
// Copyright 2016 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.
// This class is only used in the Editor, so make sure to only compile it on that platform.
// Additionally, it depends on EmulatorClientSocket which is only compiled in the editor.
// This MonoBehaviour is only ever instantiated dynamically, so it is fine that it is only compiled in the Editor,
// Otherwise it would cause serialization issues.
#if UNITY_EDITOR
using System.Collections;
using UnityEngine;
using proto;
/// @cond
namespace Gvr.Internal {
class EmulatorManager : MonoBehaviour {
private IEnumerator emulatorUpdate;
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
public static EmulatorManager Instance {
get {
if (instance == null) {
var gameObject = new GameObject("PhoneRemote");
instance = gameObject.AddComponent<EmulatorManager>();
// This object should survive all scene transitions.
GameObject.DontDestroyOnLoad(instance);
}
return instance;
}
}
private static EmulatorManager instance = null;
public delegate void OnGyroEvent(EmulatorGyroEvent gyroEvent);
public event OnGyroEvent gyroEventListeners {
add {
if (value != null) {
value(currentGyroEvent);
}
gyroEventListenersInternal += value;
}
remove {
gyroEventListenersInternal -= value;
}
}
public delegate void OnAccelEvent(EmulatorAccelEvent accelEvent);
public event OnAccelEvent accelEventListeners {
add {
if (value != null) {
value(currentAccelEvent);
}
accelEventListenersInternal += value;
}
remove {
accelEventListenersInternal -= value;
}
}
public delegate void OnTouchEvent(EmulatorTouchEvent touchEvent);
public event OnTouchEvent touchEventListeners {
add {
if (value != null
&& currentTouchEvent.pointers != null /* null only during init */) {
value(currentTouchEvent);
}
touchEventListenersInternal += value;
}
remove {
touchEventListenersInternal -= value;
}
}
public delegate void OnOrientationEvent(EmulatorOrientationEvent orientationEvent);
public event OnOrientationEvent orientationEventListeners {
add {
if (value != null) {
value(currentOrientationEvent);
}
orientationEventListenersInternal += value;
}
remove {
orientationEventListenersInternal -= value;
}
}
public delegate void OnButtonEvent(EmulatorButtonEvent buttonEvent);
public event OnButtonEvent buttonEventListeners {
add {
if (value != null) {
value(currentButtonEvent);
}
buttonEventListenersInternal += value;
}
remove {
buttonEventListenersInternal -= value;
}
}
private void onGyroEvent(EmulatorGyroEvent e) {
currentGyroEvent = e;
if (gyroEventListenersInternal != null) {
gyroEventListenersInternal(e);
}
}
private void onAccelEvent(EmulatorAccelEvent e) {
currentAccelEvent = e;
if (accelEventListenersInternal != null) {
accelEventListenersInternal(e);
}
}
private void onTouchEvent(EmulatorTouchEvent e) {
currentTouchEvent = e;
if (touchEventListenersInternal != null) {
touchEventListenersInternal(e);
}
}
private void onOrientationEvent(EmulatorOrientationEvent e) {
currentOrientationEvent = e;
if (orientationEventListenersInternal != null) {
orientationEventListenersInternal(e);
}
}
private void onButtonEvent(EmulatorButtonEvent e) {
currentButtonEvent = e;
if (buttonEventListenersInternal != null) {
buttonEventListenersInternal(e);
}
}
EmulatorGyroEvent currentGyroEvent;
EmulatorAccelEvent currentAccelEvent;
EmulatorTouchEvent currentTouchEvent;
EmulatorOrientationEvent currentOrientationEvent;
EmulatorButtonEvent currentButtonEvent;
private event OnGyroEvent gyroEventListenersInternal;
private event OnAccelEvent accelEventListenersInternal;
private event OnTouchEvent touchEventListenersInternal;
private event OnOrientationEvent orientationEventListenersInternal;
private event OnButtonEvent buttonEventListenersInternal;
private Queue pendingEvents = Queue.Synchronized(new Queue());
private EmulatorClientSocket socket;
private long lastDownTimeMs;
public bool Connected {
get {
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connected;
}
}
public bool Connecting {
get {
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connecting;
}
}
public void Awake() {
if (instance == null) {
instance = this;
}
if (instance != this) {
Debug.LogWarning("PhoneRemote must be a singleton.");
enabled = false;
return;
}
}
public void Start() {
socket = gameObject.AddComponent<EmulatorClientSocket>();
socket.Init(this);
emulatorUpdate = EndOfFrame();
StartCoroutine(emulatorUpdate);
}
IEnumerator EndOfFrame() {
while (true) {
yield return waitForEndOfFrame;
lock (pendingEvents.SyncRoot) {
while (pendingEvents.Count > 0) {
PhoneEvent phoneEvent = (PhoneEvent) pendingEvents.Dequeue();
ProcessEventAtEndOfFrame(phoneEvent);
}
}
}
}
public void OnPhoneEvent(PhoneEvent e) {
pendingEvents.Enqueue(e);
}
private void ProcessEventAtEndOfFrame(PhoneEvent e) {
switch (e.Type) {
case PhoneEvent.Types.Type.MOTION:
EmulatorTouchEvent touchEvent = new EmulatorTouchEvent(e.MotionEvent, lastDownTimeMs);
onTouchEvent(touchEvent);
if (touchEvent.getActionMasked() == EmulatorTouchEvent.Action.kActionDown) {
lastDownTimeMs = e.MotionEvent.Timestamp;
}
break;
case PhoneEvent.Types.Type.GYROSCOPE:
EmulatorGyroEvent gyroEvent = new EmulatorGyroEvent(e.GyroscopeEvent);
onGyroEvent(gyroEvent);
break;
case PhoneEvent.Types.Type.ACCELEROMETER:
EmulatorAccelEvent accelEvent = new EmulatorAccelEvent(e.AccelerometerEvent);
onAccelEvent(accelEvent);
break;
case PhoneEvent.Types.Type.ORIENTATION:
EmulatorOrientationEvent orientationEvent =
new EmulatorOrientationEvent(e.OrientationEvent);
onOrientationEvent(orientationEvent);
break;
case PhoneEvent.Types.Type.KEY:
EmulatorButtonEvent buttonEvent = new EmulatorButtonEvent(e.KeyEvent);
onButtonEvent(buttonEvent);
break;
default:
Debug.Log("Unsupported PhoneEvent type: " + e.Type);
break;
}
}
}
}
/// @endcond
#endif // UNITY_EDITOR

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c42ca6bb02b364893b127c681c158442
timeCreated: 1462051658
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 1a6b456eb0cd540a489e0f82c377b187
timeCreated: 1462046540
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,44 @@
// Copyright 2016 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 System;
/// @cond
namespace Gvr.Internal {
/// Internal interface that abstracts an implementation of a controller.
///
/// Each platform has a different concrete implementation of a Controller Provider.
/// For example, if running on the Unity Editor, we use an implementation that
/// communicates with the controller emulator via USB or WiFi. If running on a real
/// Android device, we use an implementation that uses the underlying Daydream controller API.
interface IControllerProvider : IDisposable {
/// True if controller has battery status support.
bool SupportsBatteryStatus { get; }
/// Reads the number of controllers the system is configured to use. This does not
/// indicate the number of currently connected controllers.
int MaxControllerCount { get; }
/// Notifies the controller provider that the application has paused.
void OnPause();
/// Notifies the controller provider that the application has resumed.
void OnResume();
/// Reads the controller's current state and stores it in outState.
void ReadState(ControllerState outState, int controller_id);
}
}
/// @endcond

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a546592901a23411d99a5fef0ada01e7
timeCreated: 1462043673
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ee9f54f8732db114da4ba6cefc260101
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
// 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.
// The controller is not available for versions of Unity without the
// GVR native integration.
using UnityEngine;
using System.Collections;
/// A lightweight tooltip designed to minimize draw calls.
[ExecuteInEditMode]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerTooltipsSimple")]
public class GvrControllerTooltipsSimple : MonoBehaviour, IGvrArmModelReceiver {
private MeshRenderer tooltipRenderer;
public GvrBaseArmModel ArmModel { get; set; }
private MaterialPropertyBlock materialPropertyBlock;
private int colorId;
void Awake() {
Initialize();
}
void OnEnable() {
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
}
void OnDisable() {
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
}
void OnValidate() {
if (!Application.isPlaying){
Initialize();
OnVisualUpdate();
}
}
private void Initialize(){
if(tooltipRenderer == null){
tooltipRenderer = GetComponent<MeshRenderer>();
}
if(materialPropertyBlock == null){
materialPropertyBlock = new MaterialPropertyBlock();
}
colorId = Shader.PropertyToID("_Color");
}
private void OnPostControllerInputUpdated() {
OnVisualUpdate();
}
protected void OnVisualUpdate () {
float alpha = ArmModel != null ? ArmModel.TooltipAlphaValue : 1.0f;
materialPropertyBlock.SetColor(colorId, new Color(1,1,1,alpha));
tooltipRenderer.SetPropertyBlock(materialPropertyBlock);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ef63f87a26b474e1c88f77dfc4f3aa3a
timeCreated: 1497972948
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,211 @@
// 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 UnityEngine.UI;
using System.Collections;
/// A tooltip for displaying control schemes overlaying the controller visual using a Unity Canvas.
/// Automatically changes what side of the controller the tooltip is shown on depending
/// on the handedness setting for the player.
/// Automatically fades out when the controller visual is too close or too far
/// away from the player's head.
/// Look at the prefab GvrControllerPointer to see an example of how to use this script.
[RequireComponent(typeof(CanvasGroup))]
[RequireComponent(typeof(RectTransform))]
[ExecuteInEditMode]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrTooltip")]
public class GvrTooltip : MonoBehaviour, IGvrArmModelReceiver {
/// Rotation for a tooltip when it is displayed on the right side of the controller visual.
protected static readonly Quaternion RIGHT_SIDE_ROTATION = Quaternion.Euler(0.0f, 0.0f, 0.0f);
/// Rotation for a tooltip when it is displayed on the left side of the controller visual.
protected static readonly Quaternion LEFT_SIDE_ROTATION = Quaternion.Euler(0.0f, 0.0f, 180.0f);
/// Anchor point for a tooltip, used for controlling what side the tooltip is on.
protected static readonly Vector2 SQUARE_CENTER = new Vector2(0.5f, 0.5f);
/// Pivot point for a tooltip, used for controlling what side the tooltip is on.
protected static readonly Vector2 PIVOT = new Vector2(-0.5f, 0.5f);
/// Y Position for touch pad tooltips based on the standard controller visual.
protected const float TOUCH_PAD_Y_POSITION_METERS = 0.0385f;
/// Y position for app button tooltips based on the standard controller visual.
protected const float APP_BUTTON_Y_POSITION_METERS = 0.0105f;
/// Z position for all tooltips based on the standard controller visual.
protected const float TOOLTIP_Z_POSITION_METERS = 0.0098f;
/// Options for where the controller should be displayed.
/// If set to custom, then the manually set localPosition of the tooltip is used.
/// This is useful when displaying a tooltip for a non-standard controller visual.
enum Location {
TouchPadOutside,
TouchPadInside,
AppButtonOutside,
AppButtonInside,
Custom
};
[Tooltip("The location to display the tooltip at relative to the controller visual.")]
[SerializeField]
private Location location;
[Tooltip("The text field for this tooltip.")]
[SerializeField]
private Text text;
[Tooltip("Determines if the tooltip is always visible regardless of the controller's location.")]
[SerializeField]
private bool alwaysVisible;
private bool isOnLeft = false;
private RectTransform rectTransform;
private CanvasGroup canvasGroup;
/// The text field for this tooltip.
public Text TooltipText {
get {
return text;
}
}
public GvrBaseArmModel ArmModel { get; set; }
void Awake() {
rectTransform = GetComponent<RectTransform>();
canvasGroup = GetComponent<CanvasGroup>();
isOnLeft = IsTooltipOnLeft();
RefreshTooltip();
}
void OnEnable() {
// Update using OnPostControllerInputUpdated.
// This way, the position and rotation will be correct for the entire frame
// so that it doesn't matter what order Updates get called in.
if (Application.isPlaying) {
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
}
}
void OnDisable() {
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
}
private void OnPostControllerInputUpdated() {
CheckTooltipSide();
if (canvasGroup != null && ArmModel != null) {
canvasGroup.alpha = alwaysVisible ? 1.0f : ArmModel.TooltipAlphaValue;
}
}
void OnValidate() {
rectTransform = GetComponent<RectTransform>();
RefreshTooltip();
}
#if UNITY_EDITOR
void OnRenderObject() {
if (!Application.isPlaying) {
CheckTooltipSide();
}
}
#endif // UNITY_EDITOR
/// Returns true if this tooltip is set to display on the inside of the controller.
public bool IsTooltipInside() {
switch (location) {
case Location.TouchPadInside:
case Location.AppButtonInside:
case Location.Custom:
return true;
case Location.TouchPadOutside:
case Location.AppButtonOutside:
default:
return false;
}
}
/// Returns true if the tooltip should display on the left side of the controller.
/// This will change based on the handedness of the controller, as well as if the
/// tooltip is set to display inside or outside.
public bool IsTooltipOnLeft() {
bool isInside = IsTooltipInside();
GvrSettings.UserPrefsHandedness handedness = GvrSettings.Handedness;
if (handedness == GvrSettings.UserPrefsHandedness.Left) {
return !isInside;
} else {
return isInside;
}
}
/// Refreshes how the tooltip is being displayed based on what side it is being shown on.
/// Override to add custom display functionality.
protected virtual void OnSideChanged(bool IsLocationOnLeft) {
transform.localRotation = (isOnLeft ? LEFT_SIDE_ROTATION : RIGHT_SIDE_ROTATION);
if (text != null) {
text.transform.localRotation = (IsLocationOnLeft ? LEFT_SIDE_ROTATION : RIGHT_SIDE_ROTATION);
text.alignment = (IsLocationOnLeft ? TextAnchor.MiddleRight : TextAnchor.MiddleLeft);
}
}
protected float GetMetersToCanvasScale() {
return GvrUIHelpers.GetMetersToCanvasScale(transform);
}
private Vector3 GetLocalPosition() {
float metersToCanvasScale = GetMetersToCanvasScale();
// Return early if we didn't find a valid metersToCanvasScale.
if (metersToCanvasScale == 0.0f) {
return rectTransform.anchoredPosition3D;
}
float tooltipZPosition = TOOLTIP_Z_POSITION_METERS / metersToCanvasScale;
switch (location) {
case Location.TouchPadOutside:
case Location.TouchPadInside:
float touchPadYPosition = TOUCH_PAD_Y_POSITION_METERS / metersToCanvasScale;
return new Vector3(0.0f, touchPadYPosition, tooltipZPosition);
case Location.AppButtonOutside:
case Location.AppButtonInside:
float appButtonYPosition = APP_BUTTON_Y_POSITION_METERS / metersToCanvasScale;
return new Vector3(0.0f, appButtonYPosition, tooltipZPosition);
case Location.Custom:
default:
return rectTransform.anchoredPosition3D;
}
}
private void CheckTooltipSide() {
// If handedness changes, the tooltip will switch sides.
bool newIsOnLeft = IsTooltipOnLeft();
if (newIsOnLeft != isOnLeft) {
isOnLeft = newIsOnLeft;
RefreshTooltip();
}
}
private void RefreshTooltip() {
rectTransform.anchorMax = SQUARE_CENTER;
rectTransform.anchorMax = SQUARE_CENTER;
rectTransform.pivot = PIVOT;
rectTransform.anchoredPosition3D = GetLocalPosition();
OnSideChanged(isOnLeft);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 7c52851ea0de74a228fa29a84de008ba
timeCreated: 1481935272
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: