Added VR libraries
This commit is contained in:
8
Assets/GoogleVR/Scripts/Controller/ArmModel.meta
Normal file
8
Assets/GoogleVR/Scripts/Controller/ArmModel.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86ebdf1723c09ee499974202114ef39d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
432
Assets/GoogleVR/Scripts/Controller/ArmModel/GvrArmModel.cs
Normal file
432
Assets/GoogleVR/Scripts/Controller/ArmModel/GvrArmModel.cs
Normal 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
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dda4bb2d5509e44e978d2bf56e25d7c
|
||||
timeCreated: 1471566115
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b621d217eade547b4841c4471106b6e5
|
||||
timeCreated: 1495573504
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb01f40fa958548a5b1f92a685e6d46d
|
||||
timeCreated: 1495576213
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
630
Assets/GoogleVR/Scripts/Controller/GvrControllerInput.cs
Normal file
630
Assets/GoogleVR/Scripts/Controller/GvrControllerInput.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41251d5f89d5546bb9d8ba907686b71f
|
||||
timeCreated: 1495645608
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -31123
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
250
Assets/GoogleVR/Scripts/Controller/GvrControllerInputDevice.cs
Normal file
250
Assets/GoogleVR/Scripts/Controller/GvrControllerInputDevice.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b912427c85634a3fa13020a8d93c38a
|
||||
timeCreated: 1519780588
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
163
Assets/GoogleVR/Scripts/Controller/GvrControllerReticleVisual.cs
Normal file
163
Assets/GoogleVR/Scripts/Controller/GvrControllerReticleVisual.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5cd7f7fcfc8a4c1fbc201ccc579556d
|
||||
timeCreated: 1495649159
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
347
Assets/GoogleVR/Scripts/Controller/GvrControllerVisual.cs
Normal file
347
Assets/GoogleVR/Scripts/Controller/GvrControllerVisual.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa952cbcc0eb13d4ca558b6da550ff55
|
||||
timeCreated: 1472074640
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
123
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs
Normal file
123
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
12
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs.meta
Normal file
12
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs.meta
Normal 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:
|
||||
240
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs
Normal file
240
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
12
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs.meta
Normal file
12
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs.meta
Normal 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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5d8d09b9e5d2437aa022780a2ce8c83
|
||||
timeCreated: 1487015053
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
238
Assets/GoogleVR/Scripts/Controller/GvrTrackedController.cs
Normal file
238
Assets/GoogleVR/Scripts/Controller/GvrTrackedController.cs
Normal 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
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 311793381eb9d45149dc1a422000a9fd
|
||||
timeCreated: 1481762795
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a4fdc21aee934d82baa564e797da921
|
||||
timeCreated: 1519951901
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GoogleVR/Scripts/Controller/Internal.meta
Normal file
8
Assets/GoogleVR/Scripts/Controller/Internal.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8d1351fa06562347ad7429db4b16b7c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f9210011b71142d5966eec2db6cc696
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8adfc793bee33b24b861e195c2b5b483
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fda152dc25154b4a9cccb75fd77f018
|
||||
timeCreated: 1462060442
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f791be37caef48c79f72011276ab16a
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0a32f2191dec4e1b8e05acab6002be6
|
||||
timeCreated: 1496354837
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ca644865f5f4479fb50471605078cf0
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8b9c789363a44b1e87727b1c4d085f6
|
||||
timeCreated: 1496352344
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9159532a8a3d946aa9df74e771243e5b
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8358cf789a848412fbb10ba05c4f3fce
|
||||
timeCreated: 1524508137
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c82eb0d7ee68d344bd339cec427a446
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bf1f92fb4ae24291b71e77c1ccac323
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a229fefd8ee7448b0b700f6000ebdec3
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76d2b695633884daf905c07095c8a01c
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c42ca6bb02b364893b127c681c158442
|
||||
timeCreated: 1462051658
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3506
Assets/GoogleVR/Scripts/Controller/Internal/Emulator/PhoneEvent.cs
Normal file
3506
Assets/GoogleVR/Scripts/Controller/Internal/Emulator/PhoneEvent.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a6b456eb0cd540a489e0f82c377b187
|
||||
timeCreated: 1462046540
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a546592901a23411d99a5fef0ada01e7
|
||||
timeCreated: 1462043673
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GoogleVR/Scripts/Controller/Tooltips.meta
Normal file
8
Assets/GoogleVR/Scripts/Controller/Tooltips.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee9f54f8732db114da4ba6cefc260101
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef63f87a26b474e1c88f77dfc4f3aa3a
|
||||
timeCreated: 1497972948
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
211
Assets/GoogleVR/Scripts/Controller/Tooltips/GvrTooltip.cs
Normal file
211
Assets/GoogleVR/Scripts/Controller/Tooltips/GvrTooltip.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c52851ea0de74a228fa29a84de008ba
|
||||
timeCreated: 1481935272
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user