Added VR libraries

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

View File

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

View File

@@ -0,0 +1,219 @@
// 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.EventSystems;
/// Draws a circular reticle in front of any object that the user points at.
/// The circle dilates if the object is clickable.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrReticlePointer")]
public class GvrReticlePointer : GvrBasePointer {
/// The constants below are expsed for testing. Minimum inner angle of the reticle (in degrees).
public const float RETICLE_MIN_INNER_ANGLE = 0.0f;
/// Minimum outer angle of the reticle (in degrees).
public const float RETICLE_MIN_OUTER_ANGLE = 0.5f;
/// Angle at which to expand the reticle when intersecting with an object (in degrees).
public const float RETICLE_GROWTH_ANGLE = 1.5f;
/// Minimum distance of the reticle (in meters).
public const float RETICLE_DISTANCE_MIN = 0.45f;
/// Maximum distance of the reticle (in meters).
public float maxReticleDistance = 20.0f;
/// Number of segments making the reticle circle.
public int reticleSegments = 20;
/// Growth speed multiplier for the reticle/
public float reticleGrowthSpeed = 8.0f;
/// Sorting order to use for the reticle's renderer.
/// Range values come from https://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html.
/// Default value 32767 ensures gaze reticle is always rendered on top.
[Range(-32767, 32767)]
public int reticleSortingOrder = 32767;
public Material MaterialComp { private get; set; }
// Current inner angle of the reticle (in degrees).
// Exposed for testing.
public float ReticleInnerAngle { get; private set; }
// Current outer angle of the reticle (in degrees).
// Exposed for testing.
public float ReticleOuterAngle { get; private set; }
// Current distance of the reticle (in meters).
// Getter exposed for testing.
public float ReticleDistanceInMeters { get; private set; }
// Current inner and outer diameters of the reticle, before distance multiplication.
// Getters exposed for testing.
public float ReticleInnerDiameter { get; private set; }
public float ReticleOuterDiameter { get; private set; }
public override float MaxPointerDistance { get { return maxReticleDistance; } }
public override void OnPointerEnter(RaycastResult raycastResultResult, bool isInteractive) {
SetPointerTarget(raycastResultResult.worldPosition, isInteractive);
}
public override void OnPointerHover(RaycastResult raycastResultResult, bool isInteractive) {
SetPointerTarget(raycastResultResult.worldPosition, isInteractive);
}
public override void OnPointerExit(GameObject previousObject) {
ReticleDistanceInMeters = maxReticleDistance;
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE;
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE;
}
public override void OnPointerClickDown() {}
public override void OnPointerClickUp() {}
public override void GetPointerRadius(out float enterRadius, out float exitRadius) {
float min_inner_angle_radians = Mathf.Deg2Rad * RETICLE_MIN_INNER_ANGLE;
float max_inner_angle_radians = Mathf.Deg2Rad * (RETICLE_MIN_INNER_ANGLE + RETICLE_GROWTH_ANGLE);
enterRadius = 2.0f * Mathf.Tan(min_inner_angle_radians);
exitRadius = 2.0f * Mathf.Tan(max_inner_angle_radians);
}
public void UpdateDiameters() {
ReticleDistanceInMeters =
Mathf.Clamp(ReticleDistanceInMeters, RETICLE_DISTANCE_MIN, maxReticleDistance);
if (ReticleInnerAngle < RETICLE_MIN_INNER_ANGLE) {
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE;
}
if (ReticleOuterAngle < RETICLE_MIN_OUTER_ANGLE) {
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE;
}
float inner_half_angle_radians = Mathf.Deg2Rad * ReticleInnerAngle * 0.5f;
float outer_half_angle_radians = Mathf.Deg2Rad * ReticleOuterAngle * 0.5f;
float inner_diameter = 2.0f * Mathf.Tan(inner_half_angle_radians);
float outer_diameter = 2.0f * Mathf.Tan(outer_half_angle_radians);
ReticleInnerDiameter =
Mathf.Lerp(ReticleInnerDiameter, inner_diameter, Time.unscaledDeltaTime * reticleGrowthSpeed);
ReticleOuterDiameter =
Mathf.Lerp(ReticleOuterDiameter, outer_diameter, Time.unscaledDeltaTime * reticleGrowthSpeed);
MaterialComp.SetFloat("_InnerDiameter", ReticleInnerDiameter * ReticleDistanceInMeters);
MaterialComp.SetFloat("_OuterDiameter", ReticleOuterDiameter * ReticleDistanceInMeters);
MaterialComp.SetFloat("_DistanceInMeters", ReticleDistanceInMeters);
}
void Awake() {
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE;
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE;
}
protected override void Start() {
base.Start();
Renderer rendererComponent = GetComponent<Renderer>();
rendererComponent.sortingOrder = reticleSortingOrder;
MaterialComp = rendererComponent.material;
CreateReticleVertices();
}
void Update() {
UpdateDiameters();
}
private bool SetPointerTarget(Vector3 target, bool interactive) {
if (base.PointerTransform == null) {
Debug.LogWarning("Cannot operate on a null pointer transform");
return false;
}
Vector3 targetLocalPosition = base.PointerTransform.InverseTransformPoint(target);
ReticleDistanceInMeters =
Mathf.Clamp(targetLocalPosition.z, RETICLE_DISTANCE_MIN, maxReticleDistance);
if (interactive) {
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE + RETICLE_GROWTH_ANGLE;
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE + RETICLE_GROWTH_ANGLE;
} else {
ReticleInnerAngle = RETICLE_MIN_INNER_ANGLE;
ReticleOuterAngle = RETICLE_MIN_OUTER_ANGLE;
}
return true;
}
private void CreateReticleVertices() {
Mesh mesh = new Mesh();
gameObject.AddComponent<MeshFilter>();
GetComponent<MeshFilter>().mesh = mesh;
int segments_count = reticleSegments;
int vertex_count = (segments_count+1)*2;
#region Vertices
Vector3[] vertices = new Vector3[vertex_count];
const float kTwoPi = Mathf.PI * 2.0f;
int vi = 0;
for (int si = 0; si <= segments_count; ++si) {
// Add two vertices for every circle segment: one at the beginning of the
// prism, and one at the end of the prism.
float angle = (float)si / (float)(segments_count) * kTwoPi;
float x = Mathf.Sin(angle);
float y = Mathf.Cos(angle);
vertices[vi++] = new Vector3(x, y, 0.0f); // Outer vertex.
vertices[vi++] = new Vector3(x, y, 1.0f); // Inner vertex.
}
#endregion
#region Triangles
int indices_count = (segments_count+1)*3*2;
int[] indices = new int[indices_count];
int vert = 0;
int idx = 0;
for (int si = 0; si < segments_count; ++si) {
indices[idx++] = vert+1;
indices[idx++] = vert;
indices[idx++] = vert+2;
indices[idx++] = vert+1;
indices[idx++] = vert+2;
indices[idx++] = vert+3;
vert += 2;
}
#endregion
mesh.vertices = vertices;
mesh.triangles = indices;
mesh.RecalculateBounds();
#if !UNITY_5_5_OR_NEWER
// Optimize() is deprecated as of Unity 5.5.0p1.
mesh.Optimize();
#endif // !UNITY_5_5_OR_NEWER
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,42 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// Interface for a mathematical model that uses the orientation and location
/// of the physical controller, and predicts the location of the controller and pointer
/// to determine where to place the controller model within the scene.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrBaseArmModel")]
public abstract class GvrBaseArmModel : MonoBehaviour {
/// Vector to represent the controller's location relative to
/// the user's head position.
public abstract Vector3 ControllerPositionFromHead { get; }
/// Quaternion to represent the controller's rotation relative to
/// the user's head position.
public abstract Quaternion ControllerRotationFromHead { get; }
/// The suggested rendering alpha value of the controller.
/// This is to prevent the controller from intersecting the face.
/// The range is always 0 - 1.
public abstract float PreferredAlpha { get; }
/// The suggested rendering alpha value of the controller tooltips.
/// This is to only display the tooltips when the player is looking
/// at the controller, and also to prevent the tooltips from intersecting the
/// player's face.
public abstract float TooltipAlphaValue { get; }
}

View File

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

View File

@@ -0,0 +1,17 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
public interface IGvrArmModelReceiver {
GvrBaseArmModel ArmModel { get; set; }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,163 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using UnityEngine;
using UnityEngine.Assertions;
/// Visualizes a reticle using a Quad.
/// Provides tuning options to control how the reticle scales and rotates based
/// on distance from the camera.
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshFilter))]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerReticleVisual")]
public class GvrControllerReticleVisual : MonoBehaviour {
[Serializable]
public struct FaceCameraData {
public bool alongXAxis;
public bool alongYAxis;
public bool alongZAxis;
public bool IsAnyAxisOff {
get {
return !alongXAxis || !alongYAxis || !alongZAxis;
}
}
public FaceCameraData(bool startEnabled) {
alongXAxis = startEnabled;
alongYAxis = startEnabled;
alongZAxis = startEnabled;
}
}
/// If set to false, the scale is simply set to the sizeMeters value.
[Tooltip("Determines if the size of the reticle is based on the distance from the camera.")]
public bool isSizeBasedOnCameraDistance = true;
/// The reticle will be scaled based on the size of the mesh so that it's size matches this size.
[Tooltip("Final size of the reticle in meters when it is 1 meter from the camera.")]
public float sizeMeters = 0.1f;
[Tooltip("Determines if the reticle will always face the camera and along what axes.")]
public FaceCameraData doesReticleFaceCamera = new FaceCameraData(true);
/// Sorting order to use for the reticle's renderer.
/// Range values come from https://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html.
[Range(-32767, 32767)]
public int sortingOrder = 0;
/// The size of the reticle's mesh in meters.
public float ReticleMeshSizeMeters { get; private set; }
/// The ratio of the reticleMeshSizeMeters to 1 meter.
/// If reticleMeshSizeMeters is 10, then reticleMeshSizeRatio is 0.1.
public float ReticleMeshSizeRatio { get; private set; }
protected MeshRenderer meshRenderer;
protected MeshFilter meshFilter;
private Vector3 preRenderLocalScale;
private Quaternion preRenderLocalRotation;
public void RefreshMesh() {
ReticleMeshSizeMeters = 1.0f;
ReticleMeshSizeRatio = 1.0f;
if (meshFilter != null && meshFilter.mesh != null) {
ReticleMeshSizeMeters = meshFilter.mesh.bounds.size.x;
if (ReticleMeshSizeMeters != 0.0f) {
ReticleMeshSizeRatio = 1.0f / ReticleMeshSizeMeters;
}
}
if (meshRenderer != null) {
meshRenderer.sortingOrder = sortingOrder;
}
}
protected virtual void Awake() {
meshRenderer = GetComponent<MeshRenderer>();
meshFilter = GetComponent<MeshFilter>();
}
protected virtual void OnEnable() {
RefreshMesh();
}
protected virtual void OnWillRenderObject() {
preRenderLocalScale = transform.localScale;
preRenderLocalRotation = transform.localRotation;
Camera camera = Camera.current;
UpdateReticleSize(camera);
UpdateReticleOrientation(camera);
}
protected virtual void OnRenderObject() {
// It is possible for paired calls to OnWillRenderObject/OnRenderObject to be nested if
// Camera.Render is explicitly called for any special effects. To avoid the reticle being
// rotated/scaled incorrectly in that case, the reticle is reset to it's pre-OnWillRenderObject
// after a render has finished.
transform.localScale = preRenderLocalScale;
transform.localRotation = preRenderLocalRotation;
}
protected virtual void UpdateReticleSize(Camera camera) {
if (camera == null) {
return;
}
float scale = sizeMeters;
if (isSizeBasedOnCameraDistance) {
float reticleDistanceFromCamera = (transform.position - camera.transform.position).magnitude;
scale *= ReticleMeshSizeRatio * reticleDistanceFromCamera;
}
transform.localScale = new Vector3(scale, scale, scale);
}
protected virtual void UpdateReticleOrientation(Camera camera) {
if (camera == null) {
return;
}
Vector3 direction = transform.position - camera.transform.position;
transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
if (doesReticleFaceCamera.IsAnyAxisOff) {
Vector3 euler = transform.localEulerAngles;
if (!doesReticleFaceCamera.alongXAxis) {
euler.x = 0.0f;
}
if (!doesReticleFaceCamera.alongYAxis) {
euler.y = 0.0f;
}
if (!doesReticleFaceCamera.alongZAxis) {
euler.z = 0.0f;
}
transform.localEulerAngles = euler;
}
}
protected virtual void OnValidate() {
if (Application.isPlaying && isActiveAndEnabled) {
RefreshMesh();
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,123 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The controller is not available for versions of Unity without the
// GVR native integration.
using UnityEngine;
using UnityEngine.EventSystems;
/// Implementation of GvrBasePointer for a laser pointer visual.
/// This script should be attached to the controller object.
/// The laser visual is important to help users locate their cursor
/// when its not directly in their field of view.
[RequireComponent(typeof(GvrLaserVisual))]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrLaserPointer")]
public class GvrLaserPointer : GvrBasePointer {
[Tooltip("Distance from the pointer that raycast hits will be detected.")]
public float maxPointerDistance = 20.0f;
[Tooltip("Distance from the pointer that the reticle will be drawn at when hitting nothing.")]
public float defaultReticleDistance = 20.0f;
[Tooltip("By default, the length of the laser is used as the CameraRayIntersectionDistance. " +
"Set this field to a non-zero value to override it.")]
public float overrideCameraRayIntersectionDistance;
/// The percentage of the reticle mesh that shows the reticle.
/// The rest of the reticle mesh is transparent.
private const float RETICLE_VISUAL_RATIO = 0.1f;
public GvrLaserVisual LaserVisual { get; private set; }
private bool isHittingTarget;
public override float MaxPointerDistance {
get {
return maxPointerDistance;
}
}
public override float CameraRayIntersectionDistance {
get {
if (overrideCameraRayIntersectionDistance != 0.0f) {
return overrideCameraRayIntersectionDistance;
}
return LaserVisual != null ?
LaserVisual.maxLaserDistance : overrideCameraRayIntersectionDistance;
}
}
public override void OnPointerEnter(RaycastResult raycastResult, bool isInteractive) {
LaserVisual.SetDistance(raycastResult.distance);
isHittingTarget = true;
}
public override void OnPointerHover(RaycastResult raycastResult, bool isInteractive) {
LaserVisual.SetDistance(raycastResult.distance);
isHittingTarget = true;
}
public override void OnPointerExit(GameObject previousObject) {
// Don't set the distance immediately.
// If we exit/enter an object on the same frame, then SetDistance
// will be called twice which could cause an issue with lerping the reticle.
// If we don't re-enter a new object, the distance will be set in Update.
isHittingTarget = false;
}
public override void OnPointerClickDown() {
}
public override void OnPointerClickUp() {
}
public override void GetPointerRadius(out float enterRadius, out float exitRadius) {
if (LaserVisual.reticle != null) {
float reticleScale = LaserVisual.reticle.transform.localScale.x;
// Fixed size for enter radius to avoid flickering.
// This will cause some slight variability based on the distance of the object
// from the camera, and is optimized for the average case.
enterRadius = LaserVisual.reticle.sizeMeters * 0.5f * RETICLE_VISUAL_RATIO;
// Dynamic size for exit radius.
// Always correct because we know the intersection point of the object and can
// therefore use the correct radius based on the object's distance from the camera.
exitRadius = reticleScale * LaserVisual.reticle.ReticleMeshSizeMeters * RETICLE_VISUAL_RATIO;
} else {
enterRadius = 0.0f;
exitRadius = 0.0f;
}
}
void Awake() {
LaserVisual = GetComponent<GvrLaserVisual>();
}
protected override void Start() {
base.Start();
LaserVisual.GetPointForDistanceFunction = GetPointAlongPointer;
LaserVisual.SetDistance(defaultReticleDistance, true);
}
void Update() {
if (isHittingTarget) {
return;
}
LaserVisual.SetDistance(defaultReticleDistance);
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,98 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#else
using XRSettings = UnityEngine.VR.VRSettings;
#endif // UNITY_2017_2_OR_NEWER
/// Used to recenter only the controllers, required for scenes that have no clear forward direction.
/// Details: https://developers.google.com/vr/distribute/daydream/design-requirements#UX-D6
///
/// Works by offsetting the orientation of the transform when a recenter occurs to correct for the
/// orientation change caused by the recenter event.
///
/// Usage: Place on the parent of the camera that should have it's orientation corrected.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrRecenterOnlyController")]
public class GvrRecenterOnlyController : MonoBehaviour {
private Quaternion lastAppliedYawCorrection = Quaternion.identity;
private Quaternion yawCorrection = Quaternion.identity;
void Update() {
bool connected = false;
foreach (var hand in Gvr.Internal.ControllerUtils.AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
if (device.State == GvrConnectionState.Connected) {
connected = true;
break;
}
}
if (!connected) {
return;
}
// Daydream is loaded only on deivce, not in editor.
#if UNITY_ANDROID && !UNITY_EDITOR
if (XRSettings.loadedDeviceName != GvrSettings.VR_SDK_DAYDREAM) {
return;
}
#endif
if (GvrControllerInput.Recentered) {
ApplyYawCorrection();
return;
}
#if UNITY_EDITOR
// Compatibility for Instant Preview.
if (Gvr.Internal.InstantPreview.Instance != null &&
Gvr.Internal.InstantPreview.Instance.enabled &&
Gvr.Internal.ControllerUtils.AnyButton(GvrControllerButton.System)) {
return;
}
#else // !UNITY_EDITOR
if (Gvr.Internal.ControllerUtils.AnyButton(GvrControllerButton.System)) {
return;
}
#endif // UNITY_EDITOR
yawCorrection = GetYawCorrection();
}
void OnDisable() {
yawCorrection = Quaternion.identity;
RemoveLastYawCorrection();
}
private void ApplyYawCorrection() {
RemoveLastYawCorrection();
transform.localRotation = transform.localRotation * yawCorrection;
lastAppliedYawCorrection = yawCorrection;
}
private void RemoveLastYawCorrection() {
transform.localRotation =
transform.localRotation * Quaternion.Inverse(lastAppliedYawCorrection);
lastAppliedYawCorrection = Quaternion.identity;
}
private Quaternion GetYawCorrection() {
Quaternion headRotation = GvrVRHelpers.GetHeadRotation();
Vector3 euler = headRotation.eulerAngles;
return lastAppliedYawCorrection * Quaternion.Euler(0.0f, euler.y, 0.0f);
}
}

View File

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

View File

@@ -0,0 +1,238 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using System.Collections;
/// Represents an object tracked by controller input.
/// Manages the active status of the tracked controller based on controller connection status.
/// Fetches a `GvrControllerInputDevice` for the configured `GvrControllerHand` and propagates
/// the device instance to all `IGvrControllerInputDeviceReceiver`s underneath this object on
/// Start and if the controller handedness changes. If the controller is not positionally
/// tracked, position of the object is updated to approximate arm mechanics by using a
/// `GvrBaseArmModel`. `GvrBaseArmModel`s are also propagated to all `IGvrArmModelReceiver`s
/// underneath this object.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrTrackedController")]
public class GvrTrackedController : MonoBehaviour {
[SerializeField]
[Tooltip("Arm model used to control the pose (position and rotation) of the object, " +
"and to propagate to children that implement IGvrArmModelReceiver.")]
private GvrBaseArmModel armModel;
private GvrControllerInputDevice controllerInputDevice;
[SerializeField]
[Tooltip("Is the object's active status determined by the controller connection status.")]
private bool isDeactivatedWhenDisconnected = true;
[SerializeField]
[Tooltip("Controller Hand")]
private GvrControllerHand controllerHand = GvrControllerHand.Dominant;
public GvrControllerInputDevice ControllerInputDevice {
get {
return controllerInputDevice;
}
}
public GvrControllerHand ControllerHand {
get {
return controllerHand;
}
set {
if (value != controllerHand) {
controllerHand = value;
SetupControllerInputDevice();
}
}
}
/// Arm model used to control the pose (position and rotation) of the object, and to propagate to
/// children that implement IGvrArmModelReceiver.
public GvrBaseArmModel ArmModel {
get {
return armModel;
}
set {
if (armModel == value) {
return;
}
armModel = value;
PropagateControllerInputDeviceToArmModel();
PropagateArmModel();
}
}
/// Is the object's active status determined by the controller connection status.
public bool IsDeactivatedWhenDisconnected {
get {
return isDeactivatedWhenDisconnected;
}
set {
if (isDeactivatedWhenDisconnected == value) {
return;
}
isDeactivatedWhenDisconnected = value;
if (isDeactivatedWhenDisconnected) {
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
}
public void PropagateArmModel() {
IGvrArmModelReceiver[] receivers =
GetComponentsInChildren<IGvrArmModelReceiver>(true);
for (int i = 0; i < receivers.Length; i++) {
IGvrArmModelReceiver receiver = receivers[i];
receiver.ArmModel = armModel;
}
}
void Awake() {
// Adding this event handler calls it immediately.
GvrControllerInput.OnDevicesChanged += SetupControllerInputDevice;
}
void OnEnable() {
// Print an error to console if no GvrControllerInput is found.
if (controllerInputDevice.State == GvrConnectionState.Error) {
Debug.LogWarning(controllerInputDevice.ErrorDetails);
}
// Update the position using OnPostControllerInputUpdated.
// This way, the position and rotation will be correct for the entire frame
// so that it doesn't matter what order Updates get called in.
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
/// Force the pose to update immediately in case the controller isn't updated before the next
/// time a frame is rendered.
UpdatePose();
/// Check the controller state immediately whenever this script is enabled.
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
void OnDisable() {
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
}
void Start() {
PropagateArmModel();
if (controllerInputDevice != null) {
PropagateControllerInputDevice();
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
void OnDestroy() {
GvrControllerInput.OnDevicesChanged -= SetupControllerInputDevice;
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
controllerInputDevice = null;
PropagateControllerInputDevice();
}
}
private void PropagateControllerInputDevice() {
IGvrControllerInputDeviceReceiver[] receivers =
GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
foreach (var receiver in receivers) {
receiver.ControllerInputDevice = controllerInputDevice;
}
PropagateControllerInputDeviceToArmModel();
}
private void PropagateControllerInputDeviceToArmModel() {
// Propagate the controller input device to everything in the arm model's object's
// hierarchy in case it is not a child of the tracked controller.
if (armModel != null) {
IGvrControllerInputDeviceReceiver[] receivers =
armModel.GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
foreach (var receiver in receivers) {
receiver.ControllerInputDevice = controllerInputDevice;
}
}
}
private void SetupControllerInputDevice() {
GvrControllerInputDevice newDevice = GvrControllerInput.GetDevice(controllerHand);
if (controllerInputDevice == newDevice) {
return;
}
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
controllerInputDevice = null;
}
controllerInputDevice = newDevice;
if (controllerInputDevice != null) {
controllerInputDevice.OnStateChanged += OnControllerStateChanged;
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
} else {
OnControllerStateChanged(GvrConnectionState.Disconnected, GvrConnectionState.Disconnected);
}
PropagateControllerInputDevice();
}
private void OnPostControllerInputUpdated() {
UpdatePose();
}
private void OnControllerStateChanged(GvrConnectionState state, GvrConnectionState oldState) {
if (isDeactivatedWhenDisconnected && enabled) {
gameObject.SetActive(state == GvrConnectionState.Connected);
}
}
private void UpdatePose() {
if (controllerInputDevice == null) {
return;
}
// Non-positionally tracked controllers always return Position of Vector3.zero.
if (controllerInputDevice.Position != Vector3.zero) {
transform.localPosition = controllerInputDevice.Position;
transform.localRotation = controllerInputDevice.Orientation;
} else {
if (armModel == null || !controllerInputDevice.IsDominantHand) {
return;
}
transform.localPosition = ArmModel.ControllerPositionFromHead;
transform.localRotation = ArmModel.ControllerRotationFromHead;
}
}
#if UNITY_EDITOR
/// If the "armModel" serialized field is changed while the application is playing
/// by using the inspector in the editor, then we need to call the PropagateArmModel
/// to ensure all children IGvrArmModelReceiver are updated.
/// Outside of the editor, this can't happen because the arm model can only change when
/// a Setter is called that automatically calls PropagateArmModel.
void OnValidate() {
if (Application.isPlaying && isActiveAndEnabled) {
PropagateArmModel();
if (controllerInputDevice != null) {
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
}
}
}
#endif // UNITY_EDITOR
}

View File

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

View File

@@ -0,0 +1,21 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IGvrControllerInputDeviceReceiver {
GvrControllerInputDevice ControllerInputDevice { set; }
}

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
/// @cond
namespace Gvr.Internal {
/// Factory that provides a concrete implementation of IControllerProvider for the
/// current platform.
static class ControllerProviderFactory {
/// Provides a concrete implementation of IControllerProvider appropriate for the current
/// platform. This method never returns null. In the worst case, it might return a dummy
/// provider if the platform is not supported. For demo purposes the emulator controller
/// is returned in the editor and in Standalone buids, for use inside the desktop player.
static internal IControllerProvider CreateControllerProvider(GvrControllerInput owner) {
// Use emualtor in editor, and in Standalone builds (for demo purposes).
#if UNITY_EDITOR
// Use the Editor controller provider that supports the controller emulator and the mouse.
return new EditorControllerProvider(owner.emulatorConnectionMode);
#elif UNITY_ANDROID
// Use the GVR C API.
return new AndroidNativeControllerProvider();
#else
// Platform not supported.
Debug.LogWarning("No controller support on this platform.");
return new DummyControllerProvider();
#endif // UNITY_EDITOR || UNITY_ANDROID
}
}
}
/// @endcond

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,39 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Gvr;
/// @cond
namespace Gvr.Internal {
/// Dummy controller provider.
/// Used in platforms that do not support controllers.
class DummyControllerProvider : IControllerProvider {
private ControllerState dummyState = new ControllerState();
public bool SupportsBatteryStatus {
get { return false; }
}
public int MaxControllerCount {
get { return 1; }
}
internal DummyControllerProvider() {}
public void Dispose() {}
public void ReadState(ControllerState outState,int controller_id) {
outState.CopyFrom(dummyState);
}
public void OnPause() {}
public void OnResume() {}
}
}
/// @endcond

View File

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

View File

@@ -0,0 +1,89 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This provider is only available in the editor.
#if UNITY_EDITOR
using Gvr;
namespace Gvr.Internal {
/// Controller provider used when playing in the Unity Editor.
/// Supports the Controller Emulator and Mouse input to mock the controller.
class EditorControllerProvider : IControllerProvider {
private EmulatorControllerProvider emulatorControllerProvider;
private MouseControllerProvider mouseControllerProvider;
#if UNITY_HAS_GOOGLEVR
/// Helper class to get Instant Preview controller events if connected.
private InstantPreviewControllerProvider instantPreviewControllerProvider =
new InstantPreviewControllerProvider();
#endif // UNITY_HAS_GOOGLEVR
ControllerState emulatorState = new ControllerState();
ControllerState mouseState = new ControllerState();
public bool SupportsBatteryStatus {
get { return emulatorControllerProvider.SupportsBatteryStatus; }
}
public int MaxControllerCount {
get { return 1; }
}
internal EditorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
emulatorControllerProvider = new EmulatorControllerProvider(connectionMode);
mouseControllerProvider = new MouseControllerProvider();
}
public void Dispose() {}
public void ReadState(ControllerState outState, int controller_id) {
if (controller_id != 0) {
return;
}
#if UNITY_HAS_GOOGLEVR
if (InstantPreview.Instance != null
&& InstantPreview.Instance.IsCurrentlyConnected
&& !EmulatorManager.Instance.Connected) {
// Uses Instant Preview to get controller state if connected.
instantPreviewControllerProvider.ReadState(outState);
return;
}
#endif // UNITY_HAS_GOOGLEVR
// If Instant Preview is not connected, tries to use the emulator or
// mouse.
emulatorControllerProvider.ReadState(emulatorState, controller_id);
mouseControllerProvider.ReadState(mouseState, controller_id);
// Defaults to mouse state if the emulator isn't available.
if (emulatorState.connectionState != GvrConnectionState.Connected
&& mouseState.connectionState == GvrConnectionState.Connected) {
outState.CopyFrom(mouseState);
} else {
outState.CopyFrom(emulatorState);
}
}
public void OnPause() {
emulatorControllerProvider.OnPause();
mouseControllerProvider.OnPause();
}
public void OnResume() {
emulatorControllerProvider.OnResume();
mouseControllerProvider.OnResume();
}
}
}
#endif // UNITY_EDITOR

View File

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

View File

@@ -0,0 +1,191 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This class is only used in the Editor, so make sure to only compile it on that platform.
// Additionally, it depends on EmulatorManager which is only compiled in the editor.
#if UNITY_EDITOR
using UnityEngine;
/// @cond
namespace Gvr.Internal {
/// Controller provider that connects to the controller emulator to obtain controller events.
class EmulatorControllerProvider : IControllerProvider {
private ControllerState state = new ControllerState();
/// Yaw correction due to recentering.
private Quaternion yawCorrection = Quaternion.identity;
/// True if we performed the initial recenter.
private bool initialRecenterDone = false;
/// The last (uncorrected) orientation received from the emulator.
private Quaternion lastRawOrientation = Quaternion.identity;
private GvrControllerButton lastButtonsState;
public bool SupportsBatteryStatus {
get { return true; }
}
public int MaxControllerCount {
get { return 1; }
}
/// Creates a new EmulatorControllerProvider with the specified settings.
internal EmulatorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
if (connectionMode == GvrControllerInput.EmulatorConnectionMode.USB) {
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.USB;
} else if (connectionMode == GvrControllerInput.EmulatorConnectionMode.WIFI) {
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.WIFI;
} else {
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.OFF;
}
EmulatorManager.Instance.touchEventListeners += HandleTouchEvent;
EmulatorManager.Instance.orientationEventListeners += HandleOrientationEvent;
EmulatorManager.Instance.buttonEventListeners += HandleButtonEvent;
EmulatorManager.Instance.gyroEventListeners += HandleGyroEvent;
EmulatorManager.Instance.accelEventListeners += HandleAccelEvent;
}
public void Dispose() {}
public void ReadState(ControllerState outState, int controller_id) {
if (controller_id != 0) {
return;
}
lock (state) {
state.connectionState = GvrConnectionState.Connected;
if (!EmulatorManager.Instance.Connected) {
state.connectionState = EmulatorManager.Instance.Connecting ?
GvrConnectionState.Connecting : GvrConnectionState.Disconnected;
}
state.apiStatus = EmulatorManager.Instance.Connected ? GvrControllerApiStatus.Ok :
GvrControllerApiStatus.Unavailable;
// During emulation, just assume the controller is fully charged
state.isCharging = false;
state.batteryLevel = GvrControllerBatteryLevel.Full;
state.SetButtonsUpDownFromPrevious(lastButtonsState);
lastButtonsState = state.buttonsState;
outState.CopyFrom(state);
}
state.ClearTransientState();
}
public void OnPause() {}
public void OnResume() {}
private void HandleTouchEvent(EmulatorTouchEvent touchEvent) {
if (touchEvent.pointers.Count < 1) return;
EmulatorTouchEvent.Pointer pointer = touchEvent.pointers[0];
lock (state) {
state.touchPos = new Vector2(pointer.normalizedX, pointer.normalizedY);
switch (touchEvent.getActionMasked()) {
case EmulatorTouchEvent.Action.kActionDown:
state.buttonsState |= GvrControllerButton.TouchPadTouch;
break;
case EmulatorTouchEvent.Action.kActionMove:
state.buttonsState |= GvrControllerButton.TouchPadTouch;
break;
case EmulatorTouchEvent.Action.kActionUp:
state.buttonsState &= ~GvrControllerButton.TouchPadTouch;
break;
}
}
}
private void HandleOrientationEvent(EmulatorOrientationEvent orientationEvent) {
lastRawOrientation = ConvertEmulatorQuaternion(orientationEvent.orientation);
if (!initialRecenterDone) {
Recenter();
initialRecenterDone = true;
}
lock (state) {
state.orientation = yawCorrection * lastRawOrientation;
}
}
private void HandleButtonEvent(EmulatorButtonEvent buttonEvent) {
GvrControllerButton buttonMask = 0;
switch (buttonEvent.code) {
case EmulatorButtonEvent.ButtonCode.kApp:
buttonMask = GvrControllerButton.App;
break;
case EmulatorButtonEvent.ButtonCode.kHome:
buttonMask = GvrControllerButton.System;
break;
case EmulatorButtonEvent.ButtonCode.kClick:
buttonMask = GvrControllerButton.TouchPadButton;
break;
}
if (buttonMask != 0) {
lock (state) {
state.buttonsState &= ~buttonMask;
if (buttonEvent.down) {
state.buttonsState |= buttonMask;
}
}
if (buttonMask == GvrControllerButton.System) {
if (!buttonEvent.down) {
// Finished the recentering gesture. Recenter controller.
Recenter();
}
}
}
}
private void HandleGyroEvent(EmulatorGyroEvent gyroEvent) {
lock (state) {
state.gyro = ConvertEmulatorGyro(gyroEvent.value);
}
}
private void HandleAccelEvent(EmulatorAccelEvent accelEvent) {
lock (state) {
state.accel = ConvertEmulatorAccel(accelEvent.value);
}
}
private static Quaternion ConvertEmulatorQuaternion(Quaternion emulatorQuat) {
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
return new Quaternion(emulatorQuat.x, -emulatorQuat.z, emulatorQuat.y, emulatorQuat.w);
}
private static Vector3 ConvertEmulatorGyro(Vector3 emulatorGyro) {
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
return new Vector3(-emulatorGyro.x, -emulatorGyro.z, -emulatorGyro.y);
}
private static Vector3 ConvertEmulatorAccel(Vector3 emulatorAccel) {
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
return new Vector3(emulatorAccel.x, emulatorAccel.z, emulatorAccel.y);
}
private void Recenter() {
lock (state) {
// We want the current orientation to be "forward" so, we set the yaw correction
// to undo the current rotation's yaw.
yawCorrection = Quaternion.AngleAxis(-lastRawOrientation.eulerAngles.y, Vector3.up);
state.orientation = Quaternion.identity;
state.recentered = true;
}
}
}
}
/// @endcond
#endif // UNITY_EDITOR

View File

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

View File

@@ -0,0 +1,198 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Gvr;
using UnityEngine;
namespace Gvr.Internal {
/// Mocks controller input by using the mouse.
/// The controller is connected when holding left shift.
/// Move the mouse to control gyroscope and orientation.
/// The left mouse button is used for the clickButton.
/// The right mouse button is used for the appButton.
/// The middle mouse button is used for the homeButton.
class MouseControllerProvider : IControllerProvider {
private const string AXIS_MOUSE_X = "Mouse X";
private const string AXIS_MOUSE_Y = "Mouse Y";
private ControllerState state = new ControllerState();
private Vector2 mouseDelta = new Vector2();
/// Need to store the state of the buttons from the previous frame.
/// This is because Input.GetMouseButtonDown and Input.GetMouseButtonUp
/// don't work when called after WaitForEndOfFrame, which is when ReadState is called.
private bool wasTouching;
private GvrControllerButton lastButtonsState;
private const float ROTATE_SENSITIVITY = 4.5f;
private const float TOUCH_SENSITIVITY = .12f;
private static readonly Vector3 INVERT_Y = new Vector3(1, -1, 1);
public static bool IsMouseAvailable {
get {
return Input.mousePresent && IsActivateButtonPressed;
}
}
public static bool IsActivateButtonPressed {
get {
return Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
}
}
public static bool IsClickButtonPressed {
get {
return Input.GetMouseButton(0);
}
}
public static bool IsAppButtonPressed {
get {
return Input.GetMouseButton(1);
}
}
public static bool IsHomeButtonPressed {
get {
return Input.GetMouseButton(2);
}
}
public static bool IsTouching {
get {
return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
}
}
public bool SupportsBatteryStatus {
get { return false; }
}
public int MaxControllerCount {
get { return 1; }
}
internal MouseControllerProvider() {}
public void Dispose() {}
public void ReadState(ControllerState outState, int controller_id) {
if (controller_id != 0) {
return;
}
lock (state) {
UpdateState();
outState.CopyFrom(state);
}
state.ClearTransientState();
}
public void OnPause() {}
public void OnResume() {}
private void UpdateState() {
GvrCursorHelper.ControllerEmulationActive = IsMouseAvailable;
if (!IsMouseAvailable) {
ClearState();
return;
}
state.connectionState = GvrConnectionState.Connected;
state.apiStatus = GvrControllerApiStatus.Ok;
state.isCharging = false;
state.batteryLevel = GvrControllerBatteryLevel.Full;
UpdateButtonStates();
mouseDelta.Set(
Input.GetAxis(AXIS_MOUSE_X),
Input.GetAxis(AXIS_MOUSE_Y)
);
if (0 != (state.buttonsState & GvrControllerButton.TouchPadTouch)) {
UpdateTouchPos();
} else {
UpdateOrientation();
}
}
private void UpdateTouchPos() {
Vector3 currentMousePosition = Input.mousePosition;
Vector2 touchDelta = mouseDelta * TOUCH_SENSITIVITY;
touchDelta.y *= -1.0f;
state.touchPos += touchDelta;
state.touchPos.x = Mathf.Clamp01(state.touchPos.x);
state.touchPos.y = Mathf.Clamp01(state.touchPos.y);
}
private void UpdateOrientation() {
Vector3 deltaDegrees = Vector3.Scale(mouseDelta, INVERT_Y) * ROTATE_SENSITIVITY;
state.gyro = deltaDegrees * (Mathf.Deg2Rad / Time.deltaTime);
Quaternion yaw = Quaternion.AngleAxis(deltaDegrees.x, Vector3.up);
Quaternion pitch = Quaternion.AngleAxis(deltaDegrees.y, Vector3.right);
state.orientation = state.orientation * yaw * pitch;
}
private void UpdateButtonStates() {
state.buttonsState = 0;
if (IsClickButtonPressed) {
state.buttonsState |= GvrControllerButton.TouchPadButton;
}
if (IsAppButtonPressed) {
state.buttonsState |= GvrControllerButton.App;
}
if (IsHomeButtonPressed) {
state.buttonsState |= GvrControllerButton.System;
}
if (IsTouching) {
state.buttonsState |= GvrControllerButton.TouchPadTouch;
}
state.SetButtonsUpDownFromPrevious(lastButtonsState);
lastButtonsState = state.buttonsState;
if (0 != (state.buttonsUp & GvrControllerButton.TouchPadTouch)) {
ClearTouchPos();
}
if (0 != (state.buttonsUp & GvrControllerButton.System)) {
Recenter();
}
}
private void Recenter() {
Quaternion yawCorrection = Quaternion.AngleAxis(-state.orientation.eulerAngles.y, Vector3.up);
state.orientation = state.orientation * yawCorrection;
state.recentered = true;
}
private void ClearTouchPos() {
state.touchPos = new Vector2(0.5f, 0.5f);
}
private void ClearState() {
state.connectionState = GvrConnectionState.Disconnected;
state.buttonsState = 0;
state.buttonsDown = 0;
state.buttonsUp = 0;
ClearTouchPos();
}
}
}

View File

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

View File

@@ -0,0 +1,81 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using System;
using Gvr;
/// @cond
namespace Gvr.Internal {
/// Internal representation of the controller's current state.
/// This representation is used by controller providers to represent the controller's state.
///
/// The fields in this class have identical meanings to their correspondents in the GVR C API,
/// so they are not redundantly documented here.
class ControllerState {
internal GvrConnectionState connectionState = GvrConnectionState.Disconnected;
internal GvrControllerApiStatus apiStatus = GvrControllerApiStatus.Unavailable;
internal Quaternion orientation = Quaternion.identity;
internal Vector3 position = Vector3.zero;
internal Vector3 gyro = Vector3.zero;
internal Vector3 accel = Vector3.zero;
internal Vector2 touchPos = Vector2.zero;
internal bool recentered = false;
internal GvrControllerButton buttonsState;
internal GvrControllerButton buttonsDown;
internal GvrControllerButton buttonsUp;
internal string errorDetails = "";
internal IntPtr gvrPtr = IntPtr.Zero;
internal bool isCharging = false;
internal GvrControllerBatteryLevel batteryLevel = GvrControllerBatteryLevel.Unknown;
public void CopyFrom(ControllerState other) {
connectionState = other.connectionState;
apiStatus = other.apiStatus;
orientation = other.orientation;
position = other.position;
gyro = other.gyro;
accel = other.accel;
touchPos = other.touchPos;
recentered = other.recentered;
buttonsState = other.buttonsState;
buttonsDown = other.buttonsDown;
buttonsUp = other.buttonsUp;
errorDetails = other.errorDetails;
gvrPtr = other.gvrPtr;
isCharging = other.isCharging;
batteryLevel = other.batteryLevel;
}
/// Resets the transient state (the state variables that represent events, and which are true
/// for only one frame).
public void ClearTransientState() {
recentered = false;
buttonsState = 0;
buttonsDown = 0;
buttonsUp = 0;
}
public void SetButtonsUpDownFromPrevious(GvrControllerButton prevButtonsState) {
buttonsDown = ~prevButtonsState & buttonsState;
buttonsUp = prevButtonsState & ~buttonsState;
}
}
}
/// @endcond

View File

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

View File

@@ -0,0 +1,66 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using System;
using Gvr;
/// @cond
namespace Gvr.Internal {
class ControllerUtils {
/// Convenience array of all hands.
public static GvrControllerHand[] AllHands = {
GvrControllerHand.Right,
GvrControllerHand.Left,
};
/// Returns true while the user holds down any of buttons specified in `buttons` on
/// any controller.
public static bool AnyButton(GvrControllerButton buttons) {
bool ret = false;
foreach (var hand in AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
ret |= device.GetButton(buttons);
}
return ret;
}
/// Returns true in the frame the user starts pressing down any of buttons specified
/// in `buttons` on any controller.
public static bool AnyButtonDown(GvrControllerButton buttons) {
bool ret = false;
foreach (var hand in AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
ret |= device.GetButtonDown(buttons);
}
return ret;
}
/// Returns true the frame after the user stops pressing down any of buttons specified
/// in `buttons` on any controller.
public static bool AnyButtonUp(GvrControllerButton buttons) {
bool ret = false;
foreach (var hand in AllHands) {
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
ret |= device.GetButtonUp(buttons);
}
return ret;
}
}
}
/// @endcond

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,60 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
/// @cond
namespace Gvr.Internal {
class EmulatorConfig : MonoBehaviour {
public static EmulatorConfig Instance {
get {
if (instance == null) {
EmulatorConfig[] configs = (EmulatorConfig[]) FindObjectsOfType(typeof(EmulatorConfig));
if (configs.Length == 1) {
instance = configs[0];
} else if (configs.Length > 1) {
Debug.LogError(
"Multiple PhoneRemote/Config objects in scene. Ignoring all.");
}
}
if (instance == null) {
var gameObject = new GameObject("PhoneRemoteConfig");
instance = gameObject.AddComponent<EmulatorConfig>();
DontDestroyOnLoad(instance);
}
return instance;
}
}
private static EmulatorConfig instance = null;
public enum Mode {
OFF,
USB,
WIFI,
}
// Set this value to match how the PC is connected to the phone that is
// streaming gyro, accel, and touch events. Set to OFF if using Wifi instead.
public Mode PHONE_EVENT_MODE = Mode.USB;
/*----- Internal Parameters (should not require any changes). -----*/
// IP address of the phone, when connected to the PC via USB.
public static readonly string USB_SERVER_IP = "127.0.0.1";
// IP address of the phone, when connected to the PC via WiFi.
public static readonly string WIFI_SERVER_IP = "192.168.43.1";
}
}
/// @endcond

View File

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

View File

@@ -0,0 +1,189 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using proto;
/// @cond
namespace Gvr.Internal {
struct EmulatorGyroEvent {
public readonly long timestamp;
public readonly Vector3 value;
public EmulatorGyroEvent(PhoneEvent.Types.GyroscopeEvent proto) {
timestamp = proto.Timestamp;
value = new Vector3(proto.X, proto.Y, proto.Z);
}
}
struct EmulatorAccelEvent {
public readonly long timestamp;
public readonly Vector3 value;
public EmulatorAccelEvent(PhoneEvent.Types.AccelerometerEvent proto) {
timestamp = proto.Timestamp;
value = new Vector3(proto.X, proto.Y, proto.Z);
}
}
struct EmulatorTouchEvent {
// Action constants. These should match the constants in the Android
// MotionEvent:
// http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_CANCEL
public enum Action {
kActionDown = 0,
kActionUp = 1,
kActionMove = 2,
kActionCancel = 3,
kActionPointerDown = 5,
kActionPointerUp = 6,
kActionHoverMove = 7,
kActionHoverEnter = 9,
kActionHoverExit = 10
};
// Use getActionMasked() and getActionPointer() instead.
private readonly int action;
public readonly int relativeTimestamp;
public readonly List<Pointer> pointers;
public struct Pointer {
public readonly int fingerId;
public readonly float normalizedX;
public readonly float normalizedY;
public Pointer(int fingerId, float normalizedX, float normalizedY) {
this.fingerId = fingerId;
this.normalizedX = normalizedX;
this.normalizedY = normalizedY;
}
public override string ToString () {
return string.Format ("({0}, {1}, {2})", fingerId, normalizedX,
normalizedY);
}
}
public EmulatorTouchEvent(PhoneEvent.Types.MotionEvent proto, long lastDownTimeMs) {
action = proto.Action;
relativeTimestamp =
(Action)(proto.Action & ACTION_MASK) == Action.kActionDown
? 0 : (int) (proto.Timestamp - lastDownTimeMs);
pointers = new List<Pointer>();
foreach (PhoneEvent.Types.MotionEvent.Types.Pointer pointer in
proto.PointersList) {
pointers.Add(
new Pointer(pointer.Id, pointer.NormalizedX, pointer.NormalizedY));
}
}
public EmulatorTouchEvent(Action action, int pointerId, int relativeTimestamp,
List<Pointer> pointers) {
int fingerIndex = 0;
if (action == Action.kActionPointerDown
|| action == Action.kActionPointerUp) {
fingerIndex = findPointerIndex(pointerId, pointers);
if (fingerIndex == -1) {
Debug.LogWarning("Could not find specific fingerId " + pointerId
+ " in the supplied list of pointers.");
fingerIndex = 0;
}
}
this.action = getActionUnmasked(action, fingerIndex);
this.relativeTimestamp = relativeTimestamp;
this.pointers = pointers;
}
// See Android's getActionMasked() and getActionIndex().
private static readonly int ACTION_POINTER_INDEX_SHIFT = 8;
private static readonly int ACTION_POINTER_INDEX_MASK = 0xff00;
private static readonly int ACTION_MASK = 0xff;
public Action getActionMasked() {
return (Action)(action & ACTION_MASK);
}
public Pointer getActionPointer() {
int index =
(action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
return pointers[index];
}
private static int getActionUnmasked(Action action, int fingerIndex) {
return ((int)action) | (fingerIndex << ACTION_POINTER_INDEX_SHIFT);
}
private static int findPointerIndex(int fingerId, List<Pointer> pointers) {
// Encode the fingerId info into the action, as Android does. See Android's
// getActionMasked() and getActionIndex().
int fingerIndex = -1;
for (int i = 0; i < pointers.Count; i++) {
if (fingerId == pointers[i].fingerId) {
fingerIndex = i;
break;
}
}
return fingerIndex;
}
public override string ToString () {
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
builder.AppendFormat("t = {0}; A = {1}; P = {2}; N = {3}; [",
relativeTimestamp, getActionMasked (), getActionPointer ().fingerId,
pointers.Count);
for (int i = 0; i < pointers.Count; i++) {
builder.Append(pointers[i]).Append (", ");
}
builder.Append ("]");
return builder.ToString();
}
}
struct EmulatorOrientationEvent {
public readonly long timestamp;
public readonly Quaternion orientation;
public EmulatorOrientationEvent(PhoneEvent.Types.OrientationEvent proto) {
timestamp = proto.Timestamp;
// Convert from right-handed coordinates to left-handed.
orientation = new Quaternion(proto.X, proto.Y, -proto.Z, proto.W);
}
}
struct EmulatorButtonEvent {
// Codes as reported by the IC app (reuses Android KeyEvent codes).
public enum ButtonCode {
kNone = 0,
kHome = 3, // android.view.KeyEvent.KEYCODE_HOME
kVolumeUp = 25, // android.view.KeyEvent.KEYCODE_VOLUME_UP
kVolumeDown = 24, // android.view.KeyEvent.KEYCODE_VOLUME_DOWN
kClick = 66, // android.view.KeyEvent.KEYCODE_ENTER
kApp = 82, // android.view.KeyEvent.KEYCODE_MENU
}
public readonly ButtonCode code;
public readonly bool down;
public EmulatorButtonEvent(PhoneEvent.Types.KeyEvent proto) {
code = (ButtonCode) proto.Code;
down = proto.Action == 0;
}
}
}
/// @endcond

View File

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

View File

@@ -0,0 +1,250 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This class is only used in the Editor, so make sure to only compile it on that platform.
// Additionally, it depends on EmulatorClientSocket which is only compiled in the editor.
// This MonoBehaviour is only ever instantiated dynamically, so it is fine that it is only compiled in the Editor,
// Otherwise it would cause serialization issues.
#if UNITY_EDITOR
using System.Collections;
using UnityEngine;
using proto;
/// @cond
namespace Gvr.Internal {
class EmulatorManager : MonoBehaviour {
private IEnumerator emulatorUpdate;
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
public static EmulatorManager Instance {
get {
if (instance == null) {
var gameObject = new GameObject("PhoneRemote");
instance = gameObject.AddComponent<EmulatorManager>();
// This object should survive all scene transitions.
GameObject.DontDestroyOnLoad(instance);
}
return instance;
}
}
private static EmulatorManager instance = null;
public delegate void OnGyroEvent(EmulatorGyroEvent gyroEvent);
public event OnGyroEvent gyroEventListeners {
add {
if (value != null) {
value(currentGyroEvent);
}
gyroEventListenersInternal += value;
}
remove {
gyroEventListenersInternal -= value;
}
}
public delegate void OnAccelEvent(EmulatorAccelEvent accelEvent);
public event OnAccelEvent accelEventListeners {
add {
if (value != null) {
value(currentAccelEvent);
}
accelEventListenersInternal += value;
}
remove {
accelEventListenersInternal -= value;
}
}
public delegate void OnTouchEvent(EmulatorTouchEvent touchEvent);
public event OnTouchEvent touchEventListeners {
add {
if (value != null
&& currentTouchEvent.pointers != null /* null only during init */) {
value(currentTouchEvent);
}
touchEventListenersInternal += value;
}
remove {
touchEventListenersInternal -= value;
}
}
public delegate void OnOrientationEvent(EmulatorOrientationEvent orientationEvent);
public event OnOrientationEvent orientationEventListeners {
add {
if (value != null) {
value(currentOrientationEvent);
}
orientationEventListenersInternal += value;
}
remove {
orientationEventListenersInternal -= value;
}
}
public delegate void OnButtonEvent(EmulatorButtonEvent buttonEvent);
public event OnButtonEvent buttonEventListeners {
add {
if (value != null) {
value(currentButtonEvent);
}
buttonEventListenersInternal += value;
}
remove {
buttonEventListenersInternal -= value;
}
}
private void onGyroEvent(EmulatorGyroEvent e) {
currentGyroEvent = e;
if (gyroEventListenersInternal != null) {
gyroEventListenersInternal(e);
}
}
private void onAccelEvent(EmulatorAccelEvent e) {
currentAccelEvent = e;
if (accelEventListenersInternal != null) {
accelEventListenersInternal(e);
}
}
private void onTouchEvent(EmulatorTouchEvent e) {
currentTouchEvent = e;
if (touchEventListenersInternal != null) {
touchEventListenersInternal(e);
}
}
private void onOrientationEvent(EmulatorOrientationEvent e) {
currentOrientationEvent = e;
if (orientationEventListenersInternal != null) {
orientationEventListenersInternal(e);
}
}
private void onButtonEvent(EmulatorButtonEvent e) {
currentButtonEvent = e;
if (buttonEventListenersInternal != null) {
buttonEventListenersInternal(e);
}
}
EmulatorGyroEvent currentGyroEvent;
EmulatorAccelEvent currentAccelEvent;
EmulatorTouchEvent currentTouchEvent;
EmulatorOrientationEvent currentOrientationEvent;
EmulatorButtonEvent currentButtonEvent;
private event OnGyroEvent gyroEventListenersInternal;
private event OnAccelEvent accelEventListenersInternal;
private event OnTouchEvent touchEventListenersInternal;
private event OnOrientationEvent orientationEventListenersInternal;
private event OnButtonEvent buttonEventListenersInternal;
private Queue pendingEvents = Queue.Synchronized(new Queue());
private EmulatorClientSocket socket;
private long lastDownTimeMs;
public bool Connected {
get {
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connected;
}
}
public bool Connecting {
get {
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connecting;
}
}
public void Awake() {
if (instance == null) {
instance = this;
}
if (instance != this) {
Debug.LogWarning("PhoneRemote must be a singleton.");
enabled = false;
return;
}
}
public void Start() {
socket = gameObject.AddComponent<EmulatorClientSocket>();
socket.Init(this);
emulatorUpdate = EndOfFrame();
StartCoroutine(emulatorUpdate);
}
IEnumerator EndOfFrame() {
while (true) {
yield return waitForEndOfFrame;
lock (pendingEvents.SyncRoot) {
while (pendingEvents.Count > 0) {
PhoneEvent phoneEvent = (PhoneEvent) pendingEvents.Dequeue();
ProcessEventAtEndOfFrame(phoneEvent);
}
}
}
}
public void OnPhoneEvent(PhoneEvent e) {
pendingEvents.Enqueue(e);
}
private void ProcessEventAtEndOfFrame(PhoneEvent e) {
switch (e.Type) {
case PhoneEvent.Types.Type.MOTION:
EmulatorTouchEvent touchEvent = new EmulatorTouchEvent(e.MotionEvent, lastDownTimeMs);
onTouchEvent(touchEvent);
if (touchEvent.getActionMasked() == EmulatorTouchEvent.Action.kActionDown) {
lastDownTimeMs = e.MotionEvent.Timestamp;
}
break;
case PhoneEvent.Types.Type.GYROSCOPE:
EmulatorGyroEvent gyroEvent = new EmulatorGyroEvent(e.GyroscopeEvent);
onGyroEvent(gyroEvent);
break;
case PhoneEvent.Types.Type.ACCELEROMETER:
EmulatorAccelEvent accelEvent = new EmulatorAccelEvent(e.AccelerometerEvent);
onAccelEvent(accelEvent);
break;
case PhoneEvent.Types.Type.ORIENTATION:
EmulatorOrientationEvent orientationEvent =
new EmulatorOrientationEvent(e.OrientationEvent);
onOrientationEvent(orientationEvent);
break;
case PhoneEvent.Types.Type.KEY:
EmulatorButtonEvent buttonEvent = new EmulatorButtonEvent(e.KeyEvent);
onButtonEvent(buttonEvent);
break;
default:
Debug.Log("Unsupported PhoneEvent type: " + e.Type);
break;
}
}
}
}
/// @endcond
#endif // UNITY_EDITOR

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,44 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
/// @cond
namespace Gvr.Internal {
/// Internal interface that abstracts an implementation of a controller.
///
/// Each platform has a different concrete implementation of a Controller Provider.
/// For example, if running on the Unity Editor, we use an implementation that
/// communicates with the controller emulator via USB or WiFi. If running on a real
/// Android device, we use an implementation that uses the underlying Daydream controller API.
interface IControllerProvider : IDisposable {
/// True if controller has battery status support.
bool SupportsBatteryStatus { get; }
/// Reads the number of controllers the system is configured to use. This does not
/// indicate the number of currently connected controllers.
int MaxControllerCount { get; }
/// Notifies the controller provider that the application has paused.
void OnPause();
/// Notifies the controller provider that the application has resumed.
void OnResume();
/// Reads the controller's current state and stores it in outState.
void ReadState(ControllerState outState, int controller_id);
}
}
/// @endcond

View File

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

View File

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

View File

@@ -0,0 +1,71 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The controller is not available for versions of Unity without the
// GVR native integration.
using UnityEngine;
using System.Collections;
/// A lightweight tooltip designed to minimize draw calls.
[ExecuteInEditMode]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerTooltipsSimple")]
public class GvrControllerTooltipsSimple : MonoBehaviour, IGvrArmModelReceiver {
private MeshRenderer tooltipRenderer;
public GvrBaseArmModel ArmModel { get; set; }
private MaterialPropertyBlock materialPropertyBlock;
private int colorId;
void Awake() {
Initialize();
}
void OnEnable() {
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
}
void OnDisable() {
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
}
void OnValidate() {
if (!Application.isPlaying){
Initialize();
OnVisualUpdate();
}
}
private void Initialize(){
if(tooltipRenderer == null){
tooltipRenderer = GetComponent<MeshRenderer>();
}
if(materialPropertyBlock == null){
materialPropertyBlock = new MaterialPropertyBlock();
}
colorId = Shader.PropertyToID("_Color");
}
private void OnPostControllerInputUpdated() {
OnVisualUpdate();
}
protected void OnVisualUpdate () {
float alpha = ArmModel != null ? ArmModel.TooltipAlphaValue : 1.0f;
materialPropertyBlock.SetColor(colorId, new Color(1,1,1,alpha));
tooltipRenderer.SetPropertyBlock(materialPropertyBlock);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,514 @@
// 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.EventSystems;
/// This abstract class should be implemented for pointer based input, and used with
/// the GvrPointerInputModule script.
///
/// It provides methods called on pointer interaction with in-game objects and UI,
/// trigger events, and 'BaseInputModule' class state changes.
///
/// To have the methods called, an instance of this (implemented) class must be
/// registered with the **GvrPointerManager** script in 'Start' by calling
/// GvrPointerInputModule.OnPointerCreated.
///
/// This abstract class should be implemented by pointers doing 1 of 2 things:
/// 1. Responding to movement of the users head (Cardboard gaze-based-pointer).
/// 2. Responding to the movement of the daydream controller (Daydream 3D pointer).
public abstract class GvrBasePointer : MonoBehaviour, IGvrControllerInputDeviceReceiver {
public enum RaycastMode {
/// Casts a ray from the camera through the target of the pointer.
/// This is ideal for reticles that are always rendered on top.
/// The object that is selected will always be the object that appears
/// underneath the reticle from the perspective of the camera.
/// This also prevents the reticle from appearing to "jump" when it starts/stops hitting an object.
///
/// Recommended for reticles that are always rendered on top such as the GvrReticlePointer
/// prefab which is used for cardboard apps.
///
/// Note: This will prevent the user from pointing around an object to hit something that is out of sight.
/// This isn't a problem in a typical use case.
///
/// When used with the standard daydream controller,
/// the hit detection will not account for the laser correctly for objects that are closer to the
/// camera than the end of the laser.
/// In that case, it is recommended to do one of the following things:
///
/// 1. Hide the laser.
/// 2. Use a full-length laser pointer in Direct mode.
/// 3. Use the Hybrid raycast mode.
Camera,
/// Cast a ray directly from the pointer origin.
///
/// Recommended for full-length laser pointers.
Direct,
/// Default method for casting ray.
///
/// Combines the Camera and Direct raycast modes.
/// Uses a Direct ray up until the CameraRayIntersectionDistance, and then switches to use
/// a Camera ray starting from the point where the two rays intersect.
///
/// Recommended for use with the standard settings of the GvrControllerPointer prefab.
/// This is the most versatile raycast mode. Like Camera mode, this prevents the reticle
/// appearing jumpy. Additionally, it still allows the user to target objects that are close
/// to them by using the laser as a visual reference.
Hybrid,
}
/// Represents a ray segment for a series of intersecting rays.
/// This is useful for Hybrid raycast mode, which uses two sequential rays.
public struct PointerRay {
/// The ray for this segment of the pointer.
public Ray ray;
/// The distance along the pointer from the origin of the first ray to this ray.
public float distanceFromStart;
/// Distance that this ray extends to.
public float distance;
}
/// Determines which raycast mode to use for this raycaster.
/// • Camera - Ray is cast from the camera through the pointer.
/// • Direct - Ray is cast forward from the pointer.
/// • Hybrid - Begins with a Direct ray and transitions to a Camera ray.
[Tooltip("Determines which raycast mode to use for this raycaster.\n" +
" • Camera - Ray is cast from camera.\n" +
" • Direct - Ray is cast from pointer.\n" +
" • Hybrid - Transitions from Direct ray to Camera ray.")]
public RaycastMode raycastMode = RaycastMode.Hybrid;
/// Determines the eventCamera for _GvrPointerPhysicsRaycaster_ and _GvrPointerGraphicRaycaster_.
/// Additionaly, this is used to control what camera to use when calculating the Camera ray for
/// the Hybrid and Camera raycast modes.
[Tooltip("Optional: Use a camera other than Camera.main.")]
public Camera overridePointerCamera;
#if UNITY_EDITOR
/// Determines if the rays used for raycasting will be drawn in the editor.
[Tooltip("Determines if the rays used for raycasting will be drawn in the editor.")]
public bool drawDebugRays = false;
#endif // UNITY_EDITOR
/// Convenience function to access what the pointer is currently hitting.
public RaycastResult CurrentRaycastResult {
get {
return GvrPointerInputModule.CurrentRaycastResult;
}
}
[System.Obsolete("Replaced by CurrentRaycastResult.worldPosition")]
public Vector3 PointerIntersection {
get {
RaycastResult raycastResult = CurrentRaycastResult;
return raycastResult.worldPosition;
}
}
[System.Obsolete("Replaced by CurrentRaycastResult.gameObject != null")]
public bool IsPointerIntersecting {
get {
RaycastResult raycastResult = CurrentRaycastResult;
return raycastResult.gameObject != null;
}
}
/// This is used to determine if the enterRadius or the exitRadius should be used for the raycast.
/// It is set by GvrPointerInputModule and doesn't need to be controlled manually.
public bool ShouldUseExitRadiusForRaycast { get; set; }
/// If ShouldUseExitRadiusForRaycast is true, returns the exitRadius.
/// Otherwise, returns the enterRadius.
public float CurrentPointerRadius {
get {
float enterRadius, exitRadius;
GetPointerRadius(out enterRadius, out exitRadius);
if (ShouldUseExitRadiusForRaycast) {
return exitRadius;
} else {
return enterRadius;
}
}
}
/// Returns the transform that represents this pointer.
/// It is used by GvrBasePointerRaycaster as the origin of the ray.
public virtual Transform PointerTransform {
get {
return transform;
}
}
public GvrControllerInputDevice ControllerInputDevice { get; set; }
/// If true, the trigger was just pressed. This is an event flag:
/// it will be true for only one frame after the event happens.
/// Defaults to mouse button 0 down on Cardboard or
/// ControllerInputDevice.GetButtonDown(TouchPadButton) on Daydream.
/// Can be overridden to change the trigger.
public virtual bool TriggerDown {
get {
bool isTriggerDown = Input.GetMouseButtonDown(0);
if (ControllerInputDevice != null) {
isTriggerDown |=
ControllerInputDevice.GetButtonDown(GvrControllerButton.TouchPadButton);
}
return isTriggerDown;
}
}
/// If true, the trigger is currently being pressed. This is not
/// an event: it represents the trigger's state (it remains true while the trigger is being
/// pressed).
/// Defaults to mouse button 0 state on Cardboard or
/// ControllerInputDevice.GetButton(TouchPadButton) on Daydream.
/// Can be overridden to change the trigger.
public virtual bool Triggering {
get {
bool isTriggering = Input.GetMouseButton(0);
if (ControllerInputDevice != null) {
isTriggering |=
ControllerInputDevice.GetButton(GvrControllerButton.TouchPadButton);
}
return isTriggering;
}
}
/// If true, the trigger was just released. This is an event flag:
/// it will be true for only one frame after the event happens.
/// Defaults to mouse button 0 up on Cardboard or
/// ControllerInputDevice.GetButtonUp(TouchPadButton) on Daydream.
/// Can be overridden to change the trigger.
public virtual bool TriggerUp {
get {
bool isTriggerUp = Input.GetMouseButtonUp(0);
if (ControllerInputDevice == null) {
isTriggerUp |=
ControllerInputDevice.GetButtonUp(GvrControllerButton.TouchPadButton);
}
return isTriggerUp;
}
}
/// If true, the user just started touching the touchpad. This is an event flag (it is true
/// for only one frame after the event happens, then reverts to false).
/// Used by _GvrPointerScrollInput_ to generate OnScroll events using Unity's Event System.
/// Defaults to ControllerInputDevice.GetButtonDown(TouchPadTouch), can be overridden to change
/// the input source.
public virtual bool TouchDown {
get {
if (ControllerInputDevice == null) {
return false;
} else {
return ControllerInputDevice.GetButtonDown(GvrControllerButton.TouchPadTouch);
}
}
}
/// If true, the user is currently touching the touchpad.
/// Used by _GvrPointerScrollInput_ to generate OnScroll events using Unity's Event System.
/// Defaults to ControllerInputDevice.GetButton(TouchPadTouch), can be overridden to change
/// the input source.
public virtual bool IsTouching {
get {
if (ControllerInputDevice == null) {
return false;
} else {
return ControllerInputDevice.GetButton(GvrControllerButton.TouchPadTouch);
}
}
}
/// If true, the user just stopped touching the touchpad. This is an event flag (it is true
/// for only one frame after the event happens, then reverts to false).
/// Used by _GvrPointerScrollInput_ to generate OnScroll events using Unity's Event System.
/// Defaults to ControllerInputDevice.GetButtonUp(TouchPadTouch), can be overridden to change
/// the input source.
public virtual bool TouchUp {
get {
if (ControllerInputDevice == null) {
return false;
} else {
return ControllerInputDevice.GetButtonUp(GvrControllerButton.TouchPadTouch);
}
}
}
/// 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 0 to 1.
/// (0, 0) is the top left of the touchpad and (1, 1) is the bottom right of the touchpad.
/// Used by `GvrPointerScrollInput` to generate OnScroll events using Unity's Event System.
/// Defaults to `ControllerInputDevice.TouchPos` but translated to top-left-relative coordinates
/// for backwards compatibility. Can be overridden to change the input source.
public virtual Vector2 TouchPos {
get {
if (ControllerInputDevice == null) {
return Vector2.zero;
} else {
Vector2 touchPos = ControllerInputDevice.TouchPos;
touchPos.x = (touchPos.x / 2.0f) + 0.5f;
touchPos.y = (-touchPos.y / 2.0f) + 0.5f;
return touchPos;
}
}
}
/// Returns the end point of the pointer when it is MaxPointerDistance away from the origin.
public virtual Vector3 MaxPointerEndPoint {
get {
Transform pointerTransform = PointerTransform;
if (pointerTransform == null) {
return Vector3.zero;
}
Vector3 maxEndPoint = GetPointAlongPointer(MaxPointerDistance);
return maxEndPoint;
}
}
/// If true, the pointer will be used for generating input events by _GvrPointerInputModule_.
public virtual bool IsAvailable {
get {
Transform pointerTransform = PointerTransform;
if (pointerTransform == null) {
return false;
}
if (!enabled) {
return false;
}
return pointerTransform.gameObject.activeInHierarchy;
}
}
/// When using the Camera raycast mode, this is used to calculate
/// where the ray from the pointer will intersect with the ray from the camera.
public virtual float CameraRayIntersectionDistance {
get {
return MaxPointerDistance;
}
}
public Camera PointerCamera {
get {
if (overridePointerCamera != null) {
return overridePointerCamera;
}
return Camera.main;
}
}
/// Returns the max distance from the pointer that raycast hits will be detected.
public abstract float MaxPointerDistance { get; }
/// Called when the pointer is facing a valid GameObject. This can be a 3D
/// or UI element.
///
/// **raycastResult** is the hit detection result for the object being pointed at.
/// **isInteractive** is true if the object being pointed at is interactive.
public abstract void OnPointerEnter(RaycastResult raycastResult, bool isInteractive);
/// Called every frame the user is still pointing at a valid GameObject. This
/// can be a 3D or UI element.
///
/// **raycastResult** is the hit detection result for the object being pointed at.
/// **isInteractive** is true if the object being pointed at is interactive.
public abstract void OnPointerHover(RaycastResult raycastResultResult, bool isInteractive);
/// Called when the pointer no longer faces an object previously
/// intersected with a ray projected from the camera.
/// This is also called just before **OnInputModuleDisabled**
/// previousObject will be null in this case.
///
/// **previousObject** is the object that was being pointed at the previous frame.
public abstract void OnPointerExit(GameObject previousObject);
/// Called when a click is initiated.
public abstract void OnPointerClickDown();
/// Called when click is finished.
public abstract void OnPointerClickUp();
/// Return the radius of the pointer. It is used by GvrPointerPhysicsRaycaster when
/// searching for valid pointer targets. If a radius is 0, then a ray is used to find
/// a valid pointer target. Otherwise it will use a SphereCast.
/// The *enterRadius* is used for finding new targets while the *exitRadius*
/// is used to see if you are still nearby the object currently pointed at
/// to avoid a flickering effect when just at the border of the intersection.
///
/// NOTE: This is only works with GvrPointerPhysicsRaycaster. To use it with uGUI,
/// add 3D colliders to your canvas elements.
public abstract void GetPointerRadius(out float enterRadius, out float exitRadius);
/// Returns a point in worldspace a specified distance along the pointer.
/// What this point will be is different depending on the raycastMode.
///
/// Because raycast modes differ, use this function instead of manually calculating a point
/// projected from the pointer.
public Vector3 GetPointAlongPointer(float distance) {
PointerRay pointerRay = GetRayForDistance(distance);
return pointerRay.ray.GetPoint(distance - pointerRay.distanceFromStart);
}
/// Returns the ray used for projecting points out of the pointer for the given distance.
/// In Hybrid raycast mode, the ray will be different depending upon the distance.
/// In Camera or Direct raycast mode, the ray will always be the same.
public PointerRay GetRayForDistance(float distance) {
PointerRay result = new PointerRay();
if (raycastMode == RaycastMode.Hybrid) {
float directDistance = CameraRayIntersectionDistance;
if (distance < directDistance) {
result = CalculateHybridRay(this, RaycastMode.Direct);
} else {
result = CalculateHybridRay(this, RaycastMode.Camera);
}
} else {
result = CalculateRay(this, raycastMode);
}
return result;
}
/// Calculates the ray for a given Raycast mode.
/// Will throw an exception if the raycast mode Hybrid is passed in.
/// If you need to calculate the ray for the direct or camera segment of the Hybrid raycast,
/// use CalculateHybridRay instead.
public static PointerRay CalculateRay(GvrBasePointer pointer, RaycastMode mode) {
PointerRay result = new PointerRay();
if (pointer == null || !pointer.IsAvailable) {
Debug.LogError("Cannot calculate ray when the pointer isn't available.");
return result;
}
Transform pointerTransform = pointer.PointerTransform;
if (pointerTransform == null) {
Debug.LogError("Cannot calculate ray when pointerTransform is null.");
return result;
}
result.distance = pointer.MaxPointerDistance;
switch (mode) {
case RaycastMode.Camera:
Camera camera = pointer.PointerCamera;
if (camera == null) {
Debug.LogError("Cannot calculate ray because pointer.PointerCamera is null." +
"To fix this, either tag a Camera as \"MainCamera\" or set overridePointerCamera.");
return result;
}
Vector3 rayPointerStart = pointerTransform.position;
Vector3 rayPointerEnd = rayPointerStart +
(pointerTransform.forward * pointer.CameraRayIntersectionDistance);
Vector3 cameraLocation = camera.transform.position;
Vector3 finalRayDirection = rayPointerEnd - cameraLocation;
finalRayDirection.Normalize();
Vector3 finalRayStart = cameraLocation + (finalRayDirection * camera.nearClipPlane);
result.ray = new Ray(finalRayStart, finalRayDirection);
break;
case RaycastMode.Direct:
result.ray = new Ray(pointerTransform.position, pointerTransform.forward);
break;
default:
throw new UnityException("Invalid RaycastMode " + mode + " passed into CalculateRay.");
}
return result;
}
/// Calculates the ray for the segment of the Hybrid raycast determined by the raycast mode
/// passed in. Throws an exception if Hybrid is passed in.
public static PointerRay CalculateHybridRay(GvrBasePointer pointer, RaycastMode hybridMode) {
PointerRay result;
switch (hybridMode) {
case RaycastMode.Direct:
result = CalculateRay(pointer, hybridMode);
result.distance = pointer.CameraRayIntersectionDistance;
break;
case RaycastMode.Camera:
result = CalculateRay(pointer, hybridMode);
PointerRay directRay = CalculateHybridRay(pointer, RaycastMode.Direct);
result.ray.origin = directRay.ray.GetPoint(directRay.distance);
result.distanceFromStart = directRay.distance;
result.distance = pointer.MaxPointerDistance - directRay.distance;
break;
default:
throw new UnityException("Invalid RaycastMode " + hybridMode + " passed into CalculateHybridRay.");
}
return result;
}
protected virtual void Start() {
GvrPointerInputModule.OnPointerCreated(this);
}
#if UNITY_EDITOR
protected virtual void OnDrawGizmos() {
if (drawDebugRays && Application.isPlaying && isActiveAndEnabled) {
switch (raycastMode) {
case RaycastMode.Camera:
// Camera line.
Gizmos.color = Color.green;
PointerRay pointerRay = CalculateRay(this, RaycastMode.Camera);
Gizmos.DrawLine(pointerRay.ray.origin, pointerRay.ray.GetPoint(pointerRay.distance));
Camera camera = PointerCamera;
// Pointer to intersection dotted line.
Vector3 intersection =
PointerTransform.position + (PointerTransform.forward * CameraRayIntersectionDistance);
UnityEditor.Handles.DrawDottedLine(PointerTransform.position, intersection, 1.0f);
break;
case RaycastMode.Direct:
// Direct line.
Gizmos.color = Color.blue;
pointerRay = CalculateRay(this, RaycastMode.Direct);
Gizmos.DrawLine(pointerRay.ray.origin, pointerRay.ray.GetPoint(pointerRay.distance));
break;
case RaycastMode.Hybrid:
// Direct line.
Gizmos.color = Color.blue;
pointerRay = CalculateHybridRay(this, RaycastMode.Direct);
Gizmos.DrawLine(pointerRay.ray.origin, pointerRay.ray.GetPoint(pointerRay.distance));
// Camera line.
Gizmos.color = Color.green;
pointerRay = CalculateHybridRay(this, RaycastMode.Camera);
Gizmos.DrawLine(pointerRay.ray.origin, pointerRay.ray.GetPoint(pointerRay.distance));
// Camera to intersection dotted line.
camera = PointerCamera;
if (camera != null) {
UnityEditor.Handles.DrawDottedLine(camera.transform.position, pointerRay.ray.origin, 1.0f);
}
break;
default:
break;
}
}
}
#endif // UNITY_EDITOR
}

View File

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

View File

@@ -0,0 +1,66 @@
// 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 UnityEngine.EventSystems;
using System.Collections.Generic;
/// This script provides shared functionality used by all Gvr raycasters.
public abstract class GvrBasePointerRaycaster : BaseRaycaster {
private GvrBasePointer.PointerRay lastRay;
protected GvrBasePointer.RaycastMode CurrentRaycastModeForHybrid { get; private set; }
protected GvrBasePointerRaycaster() {
}
public GvrBasePointer.PointerRay GetLastRay() {
return lastRay;
}
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList) {
GvrBasePointer pointer = GvrPointerInputModule.Pointer;
if (pointer == null || !pointer.IsAvailable) {
return;
}
if (pointer.raycastMode == GvrBasePointer.RaycastMode.Hybrid) {
RaycastHybrid(pointer, eventData, resultAppendList);
} else {
RaycastDefault(pointer, eventData, resultAppendList);
}
}
protected abstract bool PerformRaycast(GvrBasePointer.PointerRay pointerRay, float radius,
PointerEventData eventData, List<RaycastResult> resultAppendList);
private void RaycastHybrid(GvrBasePointer pointer, PointerEventData eventData, List<RaycastResult> resultAppendList) {
CurrentRaycastModeForHybrid = GvrBasePointer.RaycastMode.Direct;
lastRay = GvrBasePointer.CalculateHybridRay(pointer, CurrentRaycastModeForHybrid);
float radius = pointer.CurrentPointerRadius;
bool foundHit = PerformRaycast(lastRay, radius, eventData, resultAppendList);
if (!foundHit) {
CurrentRaycastModeForHybrid = GvrBasePointer.RaycastMode.Camera;
lastRay = GvrBasePointer.CalculateHybridRay(pointer, CurrentRaycastModeForHybrid);
PerformRaycast(lastRay, radius, eventData, resultAppendList);
}
}
private void RaycastDefault(GvrBasePointer pointer, PointerEventData eventData, List<RaycastResult> resultAppendList) {
lastRay = GvrBasePointer.CalculateRay(pointer, pointer.raycastMode);
float radius = pointer.CurrentPointerRadius;
PerformRaycast(lastRay, radius, eventData, resultAppendList);
}
}

View File

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

View File

@@ -0,0 +1,22 @@
// 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.EventSystems;
/// Interface to implement if you wish to receive OnGvrPointerHover callbacks.
public interface IGvrPointerHoverHandler : IEventSystemHandler {
/// Called when pointer is hovering over GameObject.
void OnGvrPointerHover(PointerEventData eventData);
}

View File

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

View File

@@ -0,0 +1,30 @@
// 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.EventSystems;
using System.Collections;
/// This script extends the standard Unity EventSystem events with Gvr specific events.
public static class GvrExecuteEventsExtension {
private static readonly ExecuteEvents.EventFunction<IGvrPointerHoverHandler> s_HoverHandler = Execute;
private static void Execute(IGvrPointerHoverHandler handler, BaseEventData eventData) {
handler.OnGvrPointerHover(ExecuteEvents.ValidateEventData<PointerEventData>(eventData));
}
public static ExecuteEvents.EventFunction<IGvrPointerHoverHandler> pointerHoverHandler {
get { return s_HoverHandler; }
}
}

View File

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

View File

@@ -0,0 +1,234 @@
// 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.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// This script provides a raycaster for use with the GvrPointerInputModule.
/// It behaves similarly to the standards Graphic raycaster, except that it utilize raycast
/// modes specifically for Gvr.
///
/// View GvrBasePointerRaycaster.cs and GvrPointerInputModule.cs for more details.
[AddComponentMenu("GoogleVR/GvrPointerGraphicRaycaster")]
[RequireComponent(typeof(Canvas))]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrPointerGraphicRaycaster")]
public class GvrPointerGraphicRaycaster : GvrBasePointerRaycaster {
public enum BlockingObjects {
None = 0,
TwoD = 1,
ThreeD = 2,
All = 3,
}
private const int NO_EVENT_MASK_SET = -1;
public bool ignoreReversedGraphics = true;
public BlockingObjects blockingObjects = BlockingObjects.ThreeD;
public LayerMask blockingMask = NO_EVENT_MASK_SET;
private Canvas targetCanvas;
private List<Graphic> raycastResults = new List<Graphic>();
private Camera cachedPointerEventCamera;
private static readonly List<Graphic> sortedGraphics = new List<Graphic>();
public override Camera eventCamera {
get {
GvrBasePointer pointer = GvrPointerInputModule.Pointer;
if (pointer == null) {
return null;
}
if (pointer.raycastMode == GvrBasePointer.RaycastMode.Hybrid) {
return GetCameraForRaycastMode(pointer, CurrentRaycastModeForHybrid);
} else {
return GetCameraForRaycastMode(pointer, pointer.raycastMode);
}
}
}
private Canvas canvas {
get {
if (targetCanvas != null)
return targetCanvas;
targetCanvas = GetComponent<Canvas>();
return targetCanvas;
}
}
protected GvrPointerGraphicRaycaster() {
}
protected override bool PerformRaycast(GvrBasePointer.PointerRay pointerRay, float radius,
PointerEventData eventData, List<RaycastResult> resultAppendList) {
if (canvas == null) {
return false;
}
if (eventCamera == null) {
return false;
}
if (canvas.renderMode != RenderMode.WorldSpace) {
Debug.LogError("GvrPointerGraphicRaycaster requires that the canvas renderMode is set to WorldSpace.");
return false;
}
float hitDistance = float.MaxValue;
if (blockingObjects != BlockingObjects.None) {
float dist = pointerRay.distance;
if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All) {
RaycastHit hit;
if (Physics.Raycast(pointerRay.ray, out hit, dist, blockingMask)) {
hitDistance = hit.distance;
}
}
if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All) {
RaycastHit2D hit = Physics2D.Raycast(pointerRay.ray.origin, pointerRay.ray.direction, dist, blockingMask);
if (hit.collider != null) {
hitDistance = hit.fraction * dist;
}
}
}
raycastResults.Clear();
Ray finalRay;
Raycast(canvas, pointerRay.ray, eventCamera, pointerRay.distance, raycastResults, out finalRay);
bool foundHit = false;
for (int index = 0; index < raycastResults.Count; index++) {
GameObject go = raycastResults[index].gameObject;
bool appendGraphic = true;
if (ignoreReversedGraphics) {
// If we have a camera compare the direction against the cameras forward.
Vector3 cameraFoward = eventCamera.transform.rotation * Vector3.forward;
Vector3 dir = go.transform.rotation * Vector3.forward;
appendGraphic = Vector3.Dot(cameraFoward, dir) > 0;
}
if (appendGraphic) {
float resultDistance = 0;
Transform trans = go.transform;
Vector3 transForward = trans.forward;
// http://geomalgorithms.com/a06-_intersect-2.html
float transDot = Vector3.Dot(transForward, trans.position - pointerRay.ray.origin);
float rayDot = Vector3.Dot(transForward, pointerRay.ray.direction);
resultDistance = transDot / rayDot;
Vector3 hitPosition = pointerRay.ray.origin + (pointerRay.ray.direction * resultDistance);
// Check to see if the go is behind the camera.
if (resultDistance < 0 || resultDistance >= hitDistance || resultDistance > pointerRay.distance) {
continue;
}
resultDistance = resultDistance + pointerRay.distanceFromStart;
Transform pointerTransform =
GvrPointerInputModule.Pointer.PointerTransform;
float delta = (hitPosition - pointerTransform.position).magnitude;
if (delta < pointerRay.distanceFromStart) {
continue;
}
RaycastResult castResult = new RaycastResult
{
gameObject = go,
module = this,
distance = resultDistance,
worldPosition = hitPosition,
screenPosition = eventCamera.WorldToScreenPoint(hitPosition),
index = resultAppendList.Count,
depth = raycastResults[index].depth,
sortingLayer = canvas.sortingLayerID,
sortingOrder = canvas.sortingOrder
};
resultAppendList.Add(castResult);
foundHit = true;
}
}
return foundHit;
}
private Camera GetCameraForRaycastMode(GvrBasePointer pointer, GvrBasePointer.RaycastMode mode) {
switch (mode) {
case GvrBasePointer.RaycastMode.Direct:
if (cachedPointerEventCamera == null) {
Transform pointerTransform = GvrPointerInputModule.Pointer.PointerTransform;
cachedPointerEventCamera = pointerTransform.GetComponent<Camera>();
}
if (cachedPointerEventCamera == null) {
cachedPointerEventCamera = AddDummyCameraToPointer(pointer);
return null;
}
return cachedPointerEventCamera;
case GvrBasePointer.RaycastMode.Camera:
default:
return pointer.PointerCamera;
}
}
private Camera AddDummyCameraToPointer(GvrBasePointer pointer) {
Camera camera = pointer.PointerTransform.gameObject.AddComponent<Camera>();
camera.enabled = false;
camera.nearClipPlane = 0.01f; // Minimum Near Clip Plane.
return camera;
}
/// Perform a raycast into the screen and collect all graphics underneath it.
private static void Raycast(Canvas canvas, Ray ray, Camera cam, float distance,
List<Graphic> results, out Ray finalRay) {
Vector3 screenPoint = cam.WorldToScreenPoint(ray.GetPoint(distance));
finalRay = cam.ScreenPointToRay(screenPoint);
// Necessary for the event system
IList<Graphic> foundGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
for (int i = 0; i < foundGraphics.Count; ++i) {
Graphic graphic = foundGraphics[i];
// -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
if (graphic.depth == -1 || !graphic.raycastTarget) {
continue;
}
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, screenPoint, cam)) {
continue;
}
if (graphic.Raycast(screenPoint, cam)) {
sortedGraphics.Add(graphic);
}
}
sortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
for (int i = 0; i < sortedGraphics.Count; ++i) {
results.Add(sortedGraphics[i]);
}
sortedGraphics.Clear();
}
}

View File

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

View File

@@ -0,0 +1,181 @@
// 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.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// This script provides a raycaster for use with the GvrPointerInputModule.
/// It behaves similarly to the standards Physics raycaster, except that it utilize raycast
/// modes specifically for Gvr.
///
/// View GvrBasePointerRaycaster.cs and GvrPointerInputModule.cs for more details.
[AddComponentMenu("GoogleVR/GvrPointerPhysicsRaycaster")]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrPointerPhysicsRaycaster")]
public class GvrPointerPhysicsRaycaster : GvrBasePointerRaycaster {
/// Used to sort the raycast hits by distance.
private class HitComparer: IComparer<RaycastHit> {
public int Compare(RaycastHit lhs, RaycastHit rhs) {
return lhs.distance.CompareTo(rhs.distance);
}
}
/// Const to use for clarity when no event mask is set
protected const int NO_EVENT_MASK_SET = -1;
/// The maximum allowed value for the field maxRaycastHits.
private const int MAX_RAYCAST_HITS_MAX = 512;
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
[SerializeField]
protected LayerMask raycasterEventMask = NO_EVENT_MASK_SET;
[SerializeField]
[Range(1, MAX_RAYCAST_HITS_MAX)]
/// The max number of hits that the raycaster can detect at once.
/// They are NOT guaranteed to be ordered by distance. This value should be set to a higher number
/// than the number of objects the pointer is expected to intersect with in a single frame.
///
/// This functionality is used to prevent unnecessary memory allocation to improve performance.
/// https://docs.unity3d.com/ScriptReference/Physics.SphereCastNonAlloc.html
private int maxRaycastHits = 64;
/// Buffer of raycast hits re-used each time PerformRaycast is called.
private RaycastHit[] hits;
/// Used to sort the hits by distance.
private HitComparer hitComparer = new HitComparer();
public int MaxRaycastHits {
get {
return maxRaycastHits;
}
set {
maxRaycastHits = Mathf.Min(value, MAX_RAYCAST_HITS_MAX);
if (Application.isPlaying && hits != null && hits.Length != maxRaycastHits) {
hits = new RaycastHit[maxRaycastHits];
}
}
}
/// Camera used for masking layers and determining the screen position of the raycast result.
public override Camera eventCamera {
get {
GvrBasePointer pointer = GvrPointerInputModule.Pointer;
if (pointer == null) {
return null;
}
return pointer.PointerCamera;
}
}
/// Event mask used to determine which objects will receive events.
public int finalEventMask {
get {
return (eventCamera != null) ? eventCamera.cullingMask & eventMask : NO_EVENT_MASK_SET;
}
}
/// Layer mask used to filter events. Always combined with the camera's culling mask if a camera is used.
public LayerMask eventMask {
get {
return raycasterEventMask;
}
set {
raycasterEventMask = value;
}
}
protected GvrPointerPhysicsRaycaster() {
}
protected override void Awake() {
base.Awake();
hits = new RaycastHit[maxRaycastHits];
}
protected override bool PerformRaycast(GvrBasePointer.PointerRay pointerRay, float radius,
PointerEventData eventData, List<RaycastResult> resultAppendList) {
if (eventCamera == null) {
return false;
}
int numHits;
if (radius > 0.0f) {
numHits = Physics.SphereCastNonAlloc(pointerRay.ray, radius, hits, pointerRay.distance, finalEventMask);
} else {
numHits = Physics.RaycastNonAlloc(pointerRay.ray, hits, pointerRay.distance, finalEventMask);
}
if (numHits == 0) {
return false;
}
if (numHits == MaxRaycastHits) {
MaxRaycastHits *= 2;
Debug.LogWarningFormat("Physics Raycast/Spherecast returned {0} hits, which is the current " +
"maximum and means that some hits may have been lost. Setting maxRaycastHits to {1}. " +
"Please set maxRaycastHits to a sufficiently high value for your scene.",
numHits, MaxRaycastHits);
}
Array.Sort(hits, 0, numHits, hitComparer);
for (int i = 0; i < numHits; ++i) {
Vector3 projection = Vector3.Project(hits[i].point - pointerRay.ray.origin, pointerRay.ray.direction);
Vector3 hitPosition = projection + pointerRay.ray.origin;
float resultDistance = hits[i].distance + pointerRay.distanceFromStart;
Transform pointerTransform =
GvrPointerInputModule.Pointer.PointerTransform;
float delta = (hitPosition - pointerTransform.position).magnitude;
if (delta < pointerRay.distanceFromStart) {
continue;
}
RaycastResult result = new RaycastResult
{
gameObject = hits[i].collider.gameObject,
module = this,
distance = resultDistance,
worldPosition = hitPosition,
worldNormal = hits[i].normal,
screenPosition = eventCamera.WorldToScreenPoint(hitPosition),
index = resultAppendList.Count,
sortingLayer = 0,
sortingOrder = 0
};
resultAppendList.Add(result);
}
return true;
}
#if UNITY_EDITOR
protected override void OnValidate() {
base.OnValidate();
// Makes sure that the hits buffer is updated if maxRaycastHits is changed in the inspector
// while testing in the editor.
if (Application.isPlaying) {
MaxRaycastHits = maxRaycastHits;
}
}
#endif // UNITY_EDITOR
}

View File

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

View File

@@ -0,0 +1,283 @@
// 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.EventSystems;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
/// This class is used by _GvrPointerInputModule_ to route scroll events through Unity's Event System.
/// It maintains indepedent velocities for each instance of _IScrollHandler_ that is currently being scrolled.
/// Inertia can optionally be toggled off.
[System.Serializable]
public class GvrPointerScrollInput {
public const string PROPERTY_NAME_INERTIA = "inertia";
public const string PROPERTY_NAME_DECELERATION_RATE = "decelerationRate";
private class ScrollInfo {
public bool isScrollingX = false;
public bool isScrollingY = false;
public Vector2 initScroll = Vector2.zero;
public Vector2 lastScroll = Vector2.zero;
public Vector2 scrollVelocity = Vector2.zero;
public IGvrScrollSettings scrollSettings = null;
public bool IsScrolling {
get {
return isScrollingX || isScrollingY;
}
}
}
/// Inertia means that scroll events will continue for a while after the user stops
/// touching the touchpad. It gradually slows down according to the decelerationRate.
[Tooltip("Determines if movement inertia is enabled.")]
public bool inertia = true;
/// The deceleration rate is the speed reduction per second.
/// A value of 0.5 halves the speed each second. The default is 0.05.
/// The deceleration rate is only used when inertia is enabled.
[Tooltip("The rate at which movement slows down.")]
public float decelerationRate = 0.05f;
/// Multiplier for calculating the scroll delta so that the scroll delta is
/// within the order of magnitude that the UI system expects.
public const float SCROLL_DELTA_MULTIPLIER = 1000.0f;
private const float CUTOFF_HZ = 10.0f;
private const float RC = (float) (1.0 / (2.0 * Mathf.PI * CUTOFF_HZ));
private const float SPEED_CLAMP_RATIO = 0.05f;
private const float SPEED_CLAMP = (SPEED_CLAMP_RATIO * SCROLL_DELTA_MULTIPLIER);
private const float SPEED_CLAMP_SQUARED = SPEED_CLAMP * SPEED_CLAMP;
private const float INERTIA_THRESHOLD_RATIO = 0.2f;
private const float INERTIA_THRESHOLD = (INERTIA_THRESHOLD_RATIO * SCROLL_DELTA_MULTIPLIER);
private const float INERTIA_THRESHOLD_SQUARED = INERTIA_THRESHOLD * INERTIA_THRESHOLD;
private const float SLOP_VERTICAL = 0.165f * SCROLL_DELTA_MULTIPLIER;
private const float SLOP_HORIZONTAL = 0.15f * SCROLL_DELTA_MULTIPLIER;
private Dictionary<GameObject, ScrollInfo> scrollHandlers = new Dictionary<GameObject, ScrollInfo>();
private List<GameObject> scrollingObjects = new List<GameObject>();
public void HandleScroll(GameObject currentGameObject, PointerEventData pointerData,
GvrBasePointer pointer, IGvrEventExecutor eventExecutor) {
bool touchDown = false;
bool touching = false;
bool touchUp = false;
Vector2 currentScroll = Vector2.zero;
if (pointer != null && pointer.IsAvailable) {
touchDown = pointer.TouchDown;
touching = pointer.IsTouching;
touchUp = pointer.TouchUp;
currentScroll = pointer.TouchPos * SCROLL_DELTA_MULTIPLIER;
}
GameObject currentScrollHandler = eventExecutor.GetEventHandler<IScrollHandler>(currentGameObject);
if (touchDown) {
RemoveScrollHandler(currentScrollHandler);
}
if (currentScrollHandler != null && (touchDown || touching)) {
OnTouchingScrollHandler(currentScrollHandler, pointerData, currentScroll, eventExecutor);
} else if (touchUp && currentScrollHandler != null) {
OnReleaseScrollHandler(currentScrollHandler);
}
StopScrollingIfNecessary(touching, currentScrollHandler);
UpdateInertiaScrollHandlers(touching, currentScrollHandler, pointerData, eventExecutor);
}
private void OnTouchingScrollHandler(GameObject currentScrollHandler, PointerEventData pointerData,
Vector2 currentScroll, IGvrEventExecutor eventExecutor) {
ScrollInfo scrollInfo = null;
if (!scrollHandlers.ContainsKey(currentScrollHandler)) {
scrollInfo = AddScrollHandler(currentScrollHandler, currentScroll);
} else {
scrollInfo = scrollHandlers[currentScrollHandler];
}
// Detect if we should start scrolling along the x-axis based on the horizontal slop threshold.
if (CanScrollStartX(scrollInfo, currentScroll)) {
scrollInfo.isScrollingX = true;
}
// Detect if we should start scrolling along the y-axis based on the vertical slop threshold.
if (CanScrollStartY(scrollInfo, currentScroll)) {
scrollInfo.isScrollingY = true;
}
if (scrollInfo.IsScrolling) {
Vector2 clampedScroll = currentScroll;
Vector2 clampedLastScroll = scrollInfo.lastScroll;
if (!scrollInfo.isScrollingX) {
clampedScroll.x = 0.0f;
clampedLastScroll.x = 0.0f;
}
if (!scrollInfo.isScrollingY) {
clampedScroll.y = 0.0f;
clampedLastScroll.y = 0.0f;
}
Vector2 scrollDisplacement = clampedScroll - clampedLastScroll;
UpdateVelocity(scrollInfo, scrollDisplacement);
if (!ShouldUseInertia(scrollInfo)) {
// If inertia is disabled, then we send scroll events immediately.
pointerData.scrollDelta = scrollDisplacement;
eventExecutor.ExecuteHierarchy(currentScrollHandler, pointerData, ExecuteEvents.scrollHandler);
pointerData.scrollDelta = Vector2.zero;
}
}
scrollInfo.lastScroll = currentScroll;
}
private void OnReleaseScrollHandler(GameObject currentScrollHandler) {
// When we touch up, immediately stop scrolling the currentScrollHandler if it's velocity is low.
ScrollInfo scrollInfo;
if (scrollHandlers.TryGetValue(currentScrollHandler, out scrollInfo)) {
if (!scrollInfo.IsScrolling || scrollInfo.scrollVelocity.sqrMagnitude <= INERTIA_THRESHOLD_SQUARED) {
RemoveScrollHandler(currentScrollHandler);
}
}
}
private void UpdateVelocity(ScrollInfo scrollInfo, Vector2 scrollDisplacement) {
Vector2 newVelocity = scrollDisplacement / Time.deltaTime;
float weight = Time.deltaTime / (RC + Time.deltaTime);
scrollInfo.scrollVelocity = Vector2.Lerp(scrollInfo.scrollVelocity, newVelocity, weight);
}
private void StopScrollingIfNecessary(bool touching, GameObject currentScrollHandler) {
if (scrollHandlers.Count == 0) {
return;
}
// If inertia is disabled, stop scrolling any scrollHandler that isn't currently being touched.
for (int i = scrollingObjects.Count - 1; i >= 0; i--) {
GameObject scrollHandler = scrollingObjects[i];
ScrollInfo scrollInfo = scrollHandlers[scrollHandler];
bool isScrollling = scrollInfo.IsScrolling;
bool isVelocityBelowThreshold =
isScrollling && scrollInfo.scrollVelocity.sqrMagnitude <= SPEED_CLAMP_SQUARED;
bool isCurrentlyTouching = touching && scrollHandler == currentScrollHandler;
bool shouldUseInertia = ShouldUseInertia(scrollInfo);
bool shouldStopScrolling = isVelocityBelowThreshold
|| ((!shouldUseInertia || !isScrollling) && !isCurrentlyTouching);
if (shouldStopScrolling) {
RemoveScrollHandler(scrollHandler);
}
}
}
private void UpdateInertiaScrollHandlers(bool touching, GameObject currentScrollHandler,
PointerEventData pointerData, IGvrEventExecutor eventExecutor) {
if (pointerData == null) {
return;
}
// If the currentScrollHandler is null, then the currently scrolling scrollHandlers
// must still be decelerated so the function does not return early.
for (int i = 0; i < scrollingObjects.Count; i++) {
GameObject scrollHandler = scrollingObjects[i];
ScrollInfo scrollInfo = scrollHandlers[scrollHandler];
if (!ShouldUseInertia(scrollInfo)) {
continue;
}
if (scrollInfo.IsScrolling) {
// Decelerate the scrollHandler if necessary.
if (!touching || scrollHandler != currentScrollHandler) {
float finalDecelerationRate = GetDecelerationRate(scrollInfo);
scrollInfo.scrollVelocity *= Mathf.Pow(finalDecelerationRate, Time.deltaTime);
}
// Send the scroll events.
pointerData.scrollDelta = scrollInfo.scrollVelocity * Time.deltaTime;
eventExecutor.ExecuteHierarchy(scrollHandler, pointerData, ExecuteEvents.scrollHandler);
}
}
pointerData.scrollDelta = Vector2.zero;
}
private ScrollInfo AddScrollHandler(GameObject scrollHandler, Vector2 currentScroll) {
ScrollInfo scrollInfo = new ScrollInfo();
scrollInfo.initScroll = currentScroll;
scrollInfo.lastScroll = currentScroll;
scrollInfo.scrollSettings = scrollHandler.GetComponent<IGvrScrollSettings>();
scrollHandlers[scrollHandler] = scrollInfo;
scrollingObjects.Add(scrollHandler);
return scrollInfo;
}
private void RemoveScrollHandler(GameObject scrollHandler) {
// Check if it's null via object.Equals instead of doing a direct comparison
// to avoid using Unity's equality check override for UnityEngine.Objects.
// This is so that we can remove Unity objects that have been Destroyed from the dictionary,
// but will still return early when an object is actually null.
if (object.Equals(scrollHandler, null)) {
return;
}
if (!scrollHandlers.ContainsKey(scrollHandler)) {
return;
}
scrollHandlers.Remove(scrollHandler);
scrollingObjects.Remove(scrollHandler);
}
private bool ShouldUseInertia(ScrollInfo scrollInfo) {
if (scrollInfo != null && scrollInfo.scrollSettings != null) {
return scrollInfo.scrollSettings.InertiaOverride;
}
return inertia;
}
private float GetDecelerationRate(ScrollInfo scrollInfo) {
if (scrollInfo != null && scrollInfo.scrollSettings != null) {
return scrollInfo.scrollSettings.DecelerationRateOverride;
}
return decelerationRate;
}
private static bool CanScrollStartX(ScrollInfo scrollInfo, Vector2 currentScroll) {
if (scrollInfo == null) {
return false;
}
return Mathf.Abs(currentScroll.x - scrollInfo.initScroll.x) >= SLOP_HORIZONTAL;
}
private static bool CanScrollStartY(ScrollInfo scrollInfo, Vector2 currentScroll) {
if (scrollInfo == null) {
return false;
}
return Mathf.Abs(currentScroll.y - scrollInfo.initScroll.y) >= SLOP_VERTICAL;
}
}

View File

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

View File

@@ -0,0 +1,46 @@
// 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;
/// Used to override the global scroll settings in _GvrPointerScrollInput_
/// for the GameObject that this script is attached to.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrScrollSettings")]
public class GvrScrollSettings : MonoBehaviour, IGvrScrollSettings {
/// Override the Inertia property in _GvrPointerScrollInput_ for this object.
///
/// Inertia means that scroll events will continue for a while after the user stops
/// touching the touchpad. It gradually slows down according to the decelerationRate.
[Tooltip("Determines if movement inertia is enabled.")]
public bool inertiaOverride = true;
/// The deceleration rate is the speed reduction per second.
/// A value of 0.5 halves the speed each second. The default is 0.05.
/// The deceleration rate is only used when inertia is enabled.
[Tooltip("The rate at which movement slows down.")]
public float decelerationRateOverride = 0.05f;
public bool InertiaOverride {
get {
return inertiaOverride;
}
}
public float DecelerationRateOverride {
get {
return decelerationRateOverride;
}
}
}

View File

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

View File

@@ -0,0 +1,57 @@
// 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.Collections;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#else
using XRDevice = UnityEngine.VR.VRDevice;
using XRSettings = UnityEngine.VR.VRSettings;
#endif // UNITY_2017_2_OR_NEWER
// Handler for subscribing XR Unity actions to GVR Actions.
public class GvrXREventsSubscriber : MonoBehaviour {
private static GvrXREventsSubscriber instance;
private string _loadedDeviceName;
public static string loadedDeviceName {
get {
return GetInstance()._loadedDeviceName;
}
set {
GetInstance()._loadedDeviceName = value;
}
}
private static void OnDeviceLoadAction(string newLoadedDeviceName) {
loadedDeviceName = newLoadedDeviceName;
}
void Awake() {
instance = this;
_loadedDeviceName = XRSettings.loadedDeviceName;
#if UNITY_2018_3_OR_NEWER
XRDevice.deviceLoaded += OnDeviceLoadAction;
#endif // UNITY_2018_3_OR_NEWER
}
private static GvrXREventsSubscriber GetInstance() {
if (instance == null) {
GameObject gvrXREventsSubscriber = new GameObject("GvrXREventsSubscriber");
gvrXREventsSubscriber.AddComponent<GvrXREventsSubscriber>();
}
return instance;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 12a8088f6fe474f31bffbd957940f5fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
// 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;
/// Interface to implement to override the global scroll settings
/// in _GvrPointerScrollInput_ for an object.
///
/// Must be implmented by a component. It will override the scroll settings for the
/// GameObject that the component is attached to.
///
/// Can use _GvrScrollSettings_ To override scroll settings for any existing UI type,
/// or a custom UI component can implement this directly to override the scroll settings
/// for the UI component's use case.
public interface IGvrScrollSettings {
/// Override the Inertia property in _GvrPointerScrollInput_ for this object.
///
/// Inertia means that scroll events will continue for a while after the user stops
/// touching the touchpad. It gradually slows down according to the decelerationRate.
bool InertiaOverride { get; }
/// Override the DecelerationRate property in _GvrPointerScrollInput_ for this object.
///
/// The deceleration rate is the speed reduction per second.
/// A value of 0.5 halves the speed each second.
/// The deceleration rate is only used when inertia is enabled.
float DecelerationRateOverride { get; }
}

View File

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

View File

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

View File

@@ -0,0 +1,115 @@
// 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 System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// Exposes events from _GvrEventExecutor_ that are fired by _GvrPointerInputModule_ to the editor.
/// Makes it possible to handle EventSystem events globally.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrAllEventsTrigger")]
public class GvrAllEventsTrigger : MonoBehaviour {
[Serializable]
public class TriggerEvent : UnityEvent<GameObject, PointerEventData>
{}
public TriggerEvent OnPointerClick;
public TriggerEvent OnPointerDown;
public TriggerEvent OnPointerUp;
public TriggerEvent OnPointerEnter;
public TriggerEvent OnPointerExit;
public TriggerEvent OnScroll;
private bool listenersAdded;
void OnEnable() {
AddListeners();
}
void OnDisable() {
RemoveListeners();
}
void Start() {
// The eventExecutor may not be available during OnEnable when the script is first created.
AddListeners();
}
private void AddListeners() {
GvrEventExecutor eventExecutor = GvrPointerInputModule.FindEventExecutor();
if (eventExecutor == null) {
return;
}
if (listenersAdded) {
return;
}
eventExecutor.OnPointerClick += OnPointerClickHandler;
eventExecutor.OnPointerDown += OnPointerDownHandler;
eventExecutor.OnPointerUp += OnPointerUpHandler;
eventExecutor.OnPointerEnter += OnPointerEnterHandler;
eventExecutor.OnPointerExit += OnPointerExitHandler;
eventExecutor.OnScroll += OnScrollHandler;
listenersAdded = true;
}
private void RemoveListeners() {
GvrEventExecutor eventExecutor = GvrPointerInputModule.FindEventExecutor();
if (eventExecutor == null) {
return;
}
if (!listenersAdded) {
return;
}
eventExecutor.OnPointerClick -= OnPointerClickHandler;
eventExecutor.OnPointerDown -= OnPointerDownHandler;
eventExecutor.OnPointerUp -= OnPointerUpHandler;
eventExecutor.OnPointerEnter -= OnPointerEnterHandler;
eventExecutor.OnPointerExit -= OnPointerExitHandler;
eventExecutor.OnScroll -= OnScrollHandler;
listenersAdded = false;
}
private void OnPointerClickHandler(GameObject target, PointerEventData eventData) {
OnPointerClick.Invoke(target, eventData);
}
private void OnPointerDownHandler(GameObject target, PointerEventData eventData) {
OnPointerDown.Invoke(target, eventData);
}
private void OnPointerUpHandler(GameObject target, PointerEventData eventData) {
OnPointerUp.Invoke(target, eventData);
}
private void OnPointerEnterHandler(GameObject target, PointerEventData eventData) {
OnPointerEnter.Invoke(target, eventData);
}
private void OnPointerExitHandler(GameObject target, PointerEventData eventData) {
OnPointerExit.Invoke(target, eventData);
}
private void OnScrollHandler(GameObject target, PointerEventData eventData) {
OnScroll.Invoke(target, eventData);
}
}

View File

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

View File

@@ -0,0 +1,159 @@
// 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 System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// Wraps UnityEngine.EventSystems.ExecuteEvents.
/// Also, exposes event delegates to allow global handling of events.
public class GvrEventExecutor : IGvrEventExecutor {
public delegate void EventDelegate(GameObject target, PointerEventData eventData);
/// Fired when a Click occurs on any object.
public event EventDelegate OnPointerClick {
add {
AddEventDelegate<IPointerClickHandler>(value);
}
remove {
RemoveEventDelegate<IPointerClickHandler>(value);
}
}
// Fired when a Down event occurs on any object.
public event EventDelegate OnPointerDown {
add {
AddEventDelegate<IPointerDownHandler>(value);
}
remove {
RemoveEventDelegate<IPointerDownHandler>(value);
}
}
// Fired when an Up event occurs on any object.
public event EventDelegate OnPointerUp {
add {
AddEventDelegate<IPointerUpHandler>(value);
}
remove {
RemoveEventDelegate<IPointerUpHandler>(value);
}
}
// Fired when an Enter event occurs on any object.
public event EventDelegate OnPointerEnter {
add {
AddEventDelegate<IPointerEnterHandler>(value);
}
remove {
RemoveEventDelegate<IPointerEnterHandler>(value);
}
}
// Fired when an Exit event occurs on any object.
public event EventDelegate OnPointerExit {
add {
AddEventDelegate<IPointerExitHandler>(value);
}
remove {
RemoveEventDelegate<IPointerExitHandler>(value);
}
}
// Fired when a Scroll event occurs on any object.
public event EventDelegate OnScroll {
add {
AddEventDelegate<IScrollHandler>(value);
}
remove {
RemoveEventDelegate<IScrollHandler>(value);
}
}
/// Stores delegates for events.
private Dictionary<Type, EventDelegate> eventTable;
public GvrEventExecutor() {
eventTable = new Dictionary<Type, EventDelegate>();
}
public bool Execute<T>(GameObject target,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> functor)
where T : IEventSystemHandler {
bool result = ExecuteEvents.Execute<T>(target, eventData, functor);
CallEventDelegate<T>(target, eventData);
return result;
}
public GameObject ExecuteHierarchy<T>(GameObject root,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> callbackFunction)
where T : IEventSystemHandler {
GameObject result = ExecuteEvents.ExecuteHierarchy<T>(root, eventData, callbackFunction);
CallEventDelegate<T>(root, eventData);
return result;
}
public GameObject GetEventHandler<T>(GameObject root)
where T : IEventSystemHandler {
return ExecuteEvents.GetEventHandler<T>(root);
}
private void CallEventDelegate<T>(GameObject target, BaseEventData eventData)
where T : IEventSystemHandler {
Type type = typeof(T);
EventDelegate eventDelegate;
if (eventTable.TryGetValue(type, out eventDelegate)) {
PointerEventData pointerEventData = eventData as PointerEventData;
if (pointerEventData == null) {
Debug.LogError("Event data must be PointerEventData.");
return;
}
eventDelegate(target, pointerEventData);
}
}
private void AddEventDelegate<T>(EventDelegate eventDelegate) {
Type type = typeof(T);
EventDelegate existingDelegate;
if (eventTable.TryGetValue(type, out existingDelegate)) {
eventTable[type] = existingDelegate + eventDelegate;
} else {
eventTable[type] = eventDelegate;
}
}
private void RemoveEventDelegate<T>(EventDelegate eventDelegate) {
Type type = typeof(T);
EventDelegate existingDelegate;
if (!eventTable.TryGetValue(type, out existingDelegate)) {
return;
}
eventDelegate = existingDelegate - eventDelegate;
if (eventDelegate != null) {
eventTable[type] = eventDelegate;
} else {
eventTable.Remove(type);
}
}
}

View File

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

View File

@@ -0,0 +1,219 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the MIT License, you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
//
// http://www.opensource.org/licenses/mit-license.php
//
// 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.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// This script provides an implemention of Unity's `BaseInputModule` class, so
/// that Canvas-based (_uGUI_) UI elements and 3D scene objects can be
/// interacted with in a Gvr Application.
///
/// This script is intended for use with either a
/// 3D Pointer with the Daydream Controller (Recommended for Daydream),
/// or a Gaze-based-Pointer (Recommended for Cardboard).
///
/// To use, attach to the scene's **EventSystem** object. Be sure to move it above the
/// other modules, such as _TouchInputModule_ and _StandaloneInputModule_, in order
/// for the Pointer to take priority in the event system.
///
/// If you are using a **Canvas**, set the _Render Mode_ to **World Space**,
/// and add the _GvrPointerGraphicRaycaster_ script to the object.
///
/// If you'd like pointers to work with 3D scene objects, add a _GvrPointerPhysicsRaycaster_ to the main camera,
/// and add a component that implements one of the _Event_ interfaces (_EventTrigger_ will work nicely) to
/// an object with a collider.
///
/// GvrPointerInputModule emits the following events: _Enter_, _Exit_, _Down_, _Up_, _Click_, _Select_,
/// _Deselect_, _UpdateSelected_, and _GvrPointerHover_. Scroll, move, and submit/cancel events are not emitted.
///
/// To use a 3D Pointer with the Daydream Controller:
/// - Add the prefab GoogleVR/Prefabs/UI/GvrControllerPointer to your scene.
/// - Set the parent of GvrControllerPointer to the same parent as the main camera
/// (With a local position of 0,0,0).
///
/// To use a Gaze-based-pointer:
/// - Add the prefab GoogleVR/Prefabs/UI/GvrReticlePointer to your scene.
/// - Set the parent of GvrReticlePointer to the main camera.
///
[AddComponentMenu("GoogleVR/GvrPointerInputModule")]
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrPointerInputModule")]
public class GvrPointerInputModule : BaseInputModule, IGvrInputModuleController {
/// Determines whether Pointer input is active in VR Mode only (`true`), or all of the
/// time (`false`). Set to false if you plan to use direct screen taps or other
/// input when not in VR Mode.
[Tooltip("Whether Pointer input is active in VR Mode only (true), or all the time (false).")]
public bool vrModeOnly = false;
[Tooltip("Manages scroll events for the input module.")]
public GvrPointerScrollInput scrollInput = new GvrPointerScrollInput();
public GvrPointerInputModuleImpl Impl { get; private set; }
public GvrEventExecutor EventExecutor { get; private set; }
public new EventSystem eventSystem {
get {
return base.eventSystem;
}
}
public List<RaycastResult> RaycastResultCache {
get {
return m_RaycastResultCache;
}
}
public static GvrBasePointer Pointer {
get {
GvrPointerInputModule module = FindInputModule();
if (module == null || module.Impl == null) {
return null;
}
return module.Impl.Pointer;
}
set {
GvrPointerInputModule module = FindInputModule();
if (module == null || module.Impl == null) {
return;
}
module.Impl.Pointer = value;
}
}
/// GvrBasePointer calls this when it is created.
/// If a pointer hasn't already been assigned, it
/// will assign the newly created one by default.
///
/// This simplifies the common case of having only one
/// GvrBasePointer so is can be automatically hooked up
/// to the manager. If multiple GvrBasePointers are in
/// the scene, the app has to take responsibility for
/// setting which one is active.
public static void OnPointerCreated(GvrBasePointer createdPointer) {
GvrPointerInputModule module = FindInputModule();
if (module == null || module.Impl == null) {
return;
}
if (module.Impl.Pointer == null) {
module.Impl.Pointer = createdPointer;
}
}
/// Helper function to find the Event Executor that is part of
/// the input module if one exists in the scene.
public static GvrEventExecutor FindEventExecutor() {
GvrPointerInputModule gvrInputModule = FindInputModule();
if (gvrInputModule == null) {
return null;
}
return gvrInputModule.EventExecutor;
}
/// Helper function to find the input module if one exists in the
/// scene and it is the active module.
public static GvrPointerInputModule FindInputModule() {
if (EventSystem.current == null) {
return null;
}
EventSystem eventSystem = EventSystem.current;
if (eventSystem == null) {
return null;
}
GvrPointerInputModule gvrInputModule =
eventSystem.GetComponent<GvrPointerInputModule>();
return gvrInputModule;
}
/// Convenience function to access what the current RaycastResult.
public static RaycastResult CurrentRaycastResult {
get {
GvrPointerInputModule inputModule = GvrPointerInputModule.FindInputModule();
if (inputModule == null) {
return new RaycastResult();
}
if (inputModule.Impl == null) {
return new RaycastResult();
}
if (inputModule.Impl.CurrentEventData == null) {
return new RaycastResult();
}
return inputModule.Impl.CurrentEventData.pointerCurrentRaycast;
}
}
public override bool ShouldActivateModule() {
return Impl.ShouldActivateModule();
}
public override void DeactivateModule() {
Impl.DeactivateModule();
}
public override bool IsPointerOverGameObject(int pointerId) {
return Impl.IsPointerOverGameObject(pointerId);
}
public override void Process() {
UpdateImplProperties();
Impl.Process();
}
protected override void Awake() {
base.Awake();
Impl = new GvrPointerInputModuleImpl();
EventExecutor = new GvrEventExecutor();
UpdateImplProperties();
}
public bool ShouldActivate() {
return base.ShouldActivateModule();
}
public void Deactivate() {
base.DeactivateModule();
}
public new GameObject FindCommonRoot(GameObject g1, GameObject g2) {
return BaseInputModule.FindCommonRoot(g1, g2);
}
public new BaseEventData GetBaseEventData() {
return base.GetBaseEventData();
}
public new RaycastResult FindFirstRaycast(List<RaycastResult> candidates) {
return BaseInputModule.FindFirstRaycast(candidates);
}
private void UpdateImplProperties() {
if (Impl == null) {
return;
}
Impl.ScrollInput = scrollInput;
Impl.VrModeOnly = vrModeOnly;
Impl.ModuleController = this;
Impl.EventExecutor = EventExecutor;
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fcd4baceb58cc40c98e500572bede6a6
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,458 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the MIT License, you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
//
// http://www.opensource.org/licenses/mit-license.php
//
// 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.EventSystems;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#else
using XRSettings = UnityEngine.VR.VRSettings;
#endif // UNITY_2017_2_OR_NEWER
/// Implementation of _GvrPointerInputModule_
public class GvrPointerInputModuleImpl {
/// Interface for controlling the actual InputModule.
public IGvrInputModuleController ModuleController { get; set; }
/// Interface for executing events.
public IGvrEventExecutor EventExecutor { get; set; }
/// Determines whether pointer input is active in VR Mode only (`true`), or all of the
/// time (`false`). Set to false if you plan to use direct screen taps or other
/// input when not in VR Mode.
public bool VrModeOnly { get; set; }
/// The GvrPointerScrollInput used to route Scroll Events through _EventSystem_
public GvrPointerScrollInput ScrollInput { get; set; }
/// PointerEventData from the most recent frame.
public PointerEventData CurrentEventData { get; private set; }
/// The GvrBasePointer which will be responding to pointer events.
public GvrBasePointer Pointer {
get {
return pointer;
}
set {
if (pointer == value) {
return;
}
TryExitPointer();
pointer = value;
}
}
private GvrBasePointer pointer;
private Vector2 lastPose;
private bool isPointerHovering = false;
// Active state
private bool isActive = false;
public bool ShouldActivateModule() {
bool isVrModeEnabled = !VrModeOnly;
isVrModeEnabled |= XRSettings.enabled;
bool activeState = ModuleController.ShouldActivate() && isVrModeEnabled;
if (activeState != isActive) {
isActive = activeState;
}
return activeState;
}
public void DeactivateModule() {
TryExitPointer();
ModuleController.Deactivate();
if (CurrentEventData != null) {
HandlePendingClick();
HandlePointerExitAndEnter(CurrentEventData, null);
CurrentEventData = null;
}
ModuleController.eventSystem.SetSelectedGameObject(null, ModuleController.GetBaseEventData());
}
public bool IsPointerOverGameObject(int pointerId) {
return CurrentEventData != null && CurrentEventData.pointerEnter != null;
}
public void Process() {
// If the pointer is inactive, make sure it is exited if necessary.
if (!IsPointerActiveAndAvailable()) {
TryExitPointer();
}
// Save the previous Game Object
GameObject previousObject = GetCurrentGameObject();
CastRay();
UpdateCurrentObject(previousObject);
UpdatePointer(previousObject);
// True during the frame that the trigger has been pressed.
bool triggerDown = false;
// True if the trigger is held down.
bool triggering = false;
if (IsPointerActiveAndAvailable()) {
triggerDown = Pointer.TriggerDown;
triggering = Pointer.Triggering;
}
bool handlePendingClickRequired = !triggering;
// Handle input
if (!triggerDown && triggering) {
HandleDrag();
} else if (triggerDown && !CurrentEventData.eligibleForClick) {
// New trigger action.
HandleTriggerDown();
} else if (handlePendingClickRequired) {
// Check if there is a pending click to handle.
HandlePendingClick();
}
ScrollInput.HandleScroll(GetCurrentGameObject(), CurrentEventData, Pointer, EventExecutor);
}
private void CastRay() {
Vector2 currentPose = lastPose;
if (IsPointerActiveAndAvailable()) {
currentPose = GvrMathHelpers.NormalizedCartesianToSpherical(Pointer.PointerTransform.forward);
}
if (CurrentEventData == null) {
CurrentEventData = new PointerEventData(ModuleController.eventSystem);
lastPose = currentPose;
}
// Store the previous raycast result.
RaycastResult previousRaycastResult = CurrentEventData.pointerCurrentRaycast;
// The initial cast must use the enter radius.
if (IsPointerActiveAndAvailable()) {
Pointer.ShouldUseExitRadiusForRaycast = false;
}
// Cast a ray into the scene
CurrentEventData.Reset();
// Set the position to the center of the camera.
// This is only necessary if using the built-in Unity raycasters.
RaycastResult raycastResult;
CurrentEventData.position = GvrVRHelpers.GetViewportCenter();
bool isPointerActiveAndAvailable = IsPointerActiveAndAvailable();
if (isPointerActiveAndAvailable) {
RaycastAll();
raycastResult = ModuleController.FindFirstRaycast(ModuleController.RaycastResultCache);
if (Pointer.ControllerInputDevice == null || Pointer.ControllerInputDevice.IsDominantHand) {
CurrentEventData.pointerId = (int)GvrControllerHand.Dominant;
} else {
CurrentEventData.pointerId = (int)GvrControllerHand.NonDominant;
}
} else {
raycastResult = new RaycastResult();
raycastResult.Clear();
}
// If we were already pointing at an object we must check that object against the exit radius
// to make sure we are no longer pointing at it to prevent flicker.
if (previousRaycastResult.gameObject != null
&& raycastResult.gameObject != previousRaycastResult.gameObject
&& isPointerActiveAndAvailable) {
Pointer.ShouldUseExitRadiusForRaycast = true;
RaycastAll();
RaycastResult firstResult = ModuleController.FindFirstRaycast(ModuleController.RaycastResultCache);
if (firstResult.gameObject == previousRaycastResult.gameObject) {
raycastResult = firstResult;
}
}
if (raycastResult.gameObject != null && raycastResult.worldPosition == Vector3.zero) {
raycastResult.worldPosition =
GvrMathHelpers.GetIntersectionPosition(CurrentEventData.enterEventCamera, raycastResult);
}
CurrentEventData.pointerCurrentRaycast = raycastResult;
// Find the real screen position associated with the raycast
// Based on the results of the hit and the state of the pointerData.
if (raycastResult.gameObject != null) {
CurrentEventData.position = raycastResult.screenPosition;
} else if (IsPointerActiveAndAvailable() && CurrentEventData.enterEventCamera != null) {
Vector3 pointerPos = Pointer.MaxPointerEndPoint;
CurrentEventData.position = CurrentEventData.enterEventCamera.WorldToScreenPoint(pointerPos);
}
ModuleController.RaycastResultCache.Clear();
CurrentEventData.delta = currentPose - lastPose;
lastPose = currentPose;
// Check to make sure the Raycaster being used is a GvrRaycaster.
if (raycastResult.module != null
&& !(raycastResult.module is GvrPointerGraphicRaycaster)
&& !(raycastResult.module is GvrPointerPhysicsRaycaster)) {
Debug.LogWarning("Using Raycaster (Raycaster: " + raycastResult.module.GetType() +
", Object: " + raycastResult.module.name + "). It is recommended to use " +
"GvrPointerPhysicsRaycaster or GvrPointerGrahpicRaycaster with GvrPointerInputModule.");
}
}
private void UpdateCurrentObject(GameObject previousObject) {
if (CurrentEventData == null) {
return;
}
// Send enter events and update the highlight.
GameObject currentObject = GetCurrentGameObject(); // Get the pointer target
HandlePointerExitAndEnter(CurrentEventData, currentObject);
// Update the current selection, or clear if it is no longer the current object.
var selected = EventExecutor.GetEventHandler<ISelectHandler>(currentObject);
if (selected == ModuleController.eventSystem.currentSelectedGameObject) {
EventExecutor.Execute(ModuleController.eventSystem.currentSelectedGameObject, ModuleController.GetBaseEventData(),
ExecuteEvents.updateSelectedHandler);
} else {
ModuleController.eventSystem.SetSelectedGameObject(null, CurrentEventData);
}
// Execute hover event.
if (currentObject != null && currentObject == previousObject) {
EventExecutor.ExecuteHierarchy(currentObject, CurrentEventData, GvrExecuteEventsExtension.pointerHoverHandler);
}
}
private void UpdatePointer(GameObject previousObject) {
if (CurrentEventData == null) {
return;
}
GameObject currentObject = GetCurrentGameObject(); // Get the pointer target
bool isPointerActiveAndAvailable = IsPointerActiveAndAvailable();
bool isInteractive = CurrentEventData.pointerPress != null ||
EventExecutor.GetEventHandler<IPointerClickHandler>(currentObject) != null ||
EventExecutor.GetEventHandler<IDragHandler>(currentObject) != null;
if (isPointerHovering && currentObject != null && currentObject == previousObject) {
if (isPointerActiveAndAvailable) {
Pointer.OnPointerHover(CurrentEventData.pointerCurrentRaycast, isInteractive);
}
} else {
// If the object's don't match or the hovering object has been destroyed
// then the pointer has exited.
if (previousObject != null || (currentObject == null && isPointerHovering)) {
if (isPointerActiveAndAvailable) {
Pointer.OnPointerExit(previousObject);
}
isPointerHovering = false;
}
if (currentObject != null) {
if (isPointerActiveAndAvailable) {
Pointer.OnPointerEnter(CurrentEventData.pointerCurrentRaycast, isInteractive);
}
isPointerHovering = true;
}
}
}
private static bool ShouldStartDrag(Vector2 pressPos, Vector2 currentPos, float threshold, bool useDragThreshold) {
if (!useDragThreshold)
return true;
return (pressPos - currentPos).sqrMagnitude >= threshold * threshold;
}
private void HandleDrag() {
bool moving = CurrentEventData.IsPointerMoving();
bool shouldStartDrag = ShouldStartDrag(CurrentEventData.pressPosition,
CurrentEventData.position,
ModuleController.eventSystem.pixelDragThreshold,
CurrentEventData.useDragThreshold);
if (moving && shouldStartDrag && CurrentEventData.pointerDrag != null && !CurrentEventData.dragging) {
EventExecutor.Execute(CurrentEventData.pointerDrag, CurrentEventData,
ExecuteEvents.beginDragHandler);
CurrentEventData.dragging = true;
}
// Drag notification
if (CurrentEventData.dragging && moving && CurrentEventData.pointerDrag != null) {
// Before doing drag we should cancel any pointer down state
// And clear selection!
if (CurrentEventData.pointerPress != CurrentEventData.pointerDrag) {
EventExecutor.Execute(CurrentEventData.pointerPress, CurrentEventData, ExecuteEvents.pointerUpHandler);
CurrentEventData.eligibleForClick = false;
CurrentEventData.pointerPress = null;
CurrentEventData.rawPointerPress = null;
}
EventExecutor.Execute(CurrentEventData.pointerDrag, CurrentEventData, ExecuteEvents.dragHandler);
}
}
private void HandlePendingClick() {
if (CurrentEventData == null || (!CurrentEventData.eligibleForClick && !CurrentEventData.dragging)) {
return;
}
if (IsPointerActiveAndAvailable()) {
Pointer.OnPointerClickUp();
}
var go = CurrentEventData.pointerCurrentRaycast.gameObject;
// Send pointer up and click events.
EventExecutor.Execute(CurrentEventData.pointerPress, CurrentEventData, ExecuteEvents.pointerUpHandler);
GameObject pointerClickHandler = EventExecutor.GetEventHandler<IPointerClickHandler>(go);
if (CurrentEventData.pointerPress == pointerClickHandler && CurrentEventData.eligibleForClick) {
EventExecutor.Execute(CurrentEventData.pointerPress, CurrentEventData, ExecuteEvents.pointerClickHandler);
}
if (CurrentEventData != null && CurrentEventData.pointerDrag != null && CurrentEventData.dragging) {
EventExecutor.ExecuteHierarchy(go, CurrentEventData, ExecuteEvents.dropHandler);
EventExecutor.Execute(CurrentEventData.pointerDrag, CurrentEventData, ExecuteEvents.endDragHandler);
}
if (CurrentEventData != null) {
// Clear the click state.
CurrentEventData.pointerPress = null;
CurrentEventData.rawPointerPress = null;
CurrentEventData.eligibleForClick = false;
CurrentEventData.clickCount = 0;
CurrentEventData.clickTime = 0;
CurrentEventData.pointerDrag = null;
CurrentEventData.dragging = false;
}
}
private void HandleTriggerDown() {
var go = CurrentEventData.pointerCurrentRaycast.gameObject;
// Send pointer down event.
CurrentEventData.pressPosition = CurrentEventData.position;
CurrentEventData.pointerPressRaycast = CurrentEventData.pointerCurrentRaycast;
CurrentEventData.pointerPress =
EventExecutor.ExecuteHierarchy(go, CurrentEventData, ExecuteEvents.pointerDownHandler) ??
EventExecutor.GetEventHandler<IPointerClickHandler>(go);
// Save the pending click state.
CurrentEventData.rawPointerPress = go;
CurrentEventData.eligibleForClick = true;
CurrentEventData.delta = Vector2.zero;
CurrentEventData.dragging = false;
CurrentEventData.useDragThreshold = true;
CurrentEventData.clickCount = 1;
CurrentEventData.clickTime = Time.unscaledTime;
// Save the drag handler as well
CurrentEventData.pointerDrag = EventExecutor.GetEventHandler<IDragHandler>(go);
if (CurrentEventData.pointerDrag != null) {
EventExecutor.Execute(CurrentEventData.pointerDrag, CurrentEventData, ExecuteEvents.initializePotentialDrag);
}
if (IsPointerActiveAndAvailable()) {
Pointer.OnPointerClickDown();
}
}
private GameObject GetCurrentGameObject() {
if (CurrentEventData != null) {
return CurrentEventData.pointerCurrentRaycast.gameObject;
}
return null;
}
// Modified version of BaseInputModule.HandlePointerExitAndEnter that calls EventExecutor instead of
// UnityEngine.EventSystems.ExecuteEvents.
private void HandlePointerExitAndEnter(PointerEventData currentPointerData, GameObject newEnterTarget) {
// If we have no target or pointerEnter has been deleted then
// just send exit events to anything we are tracking.
// Afterwards, exit.
if (newEnterTarget == null || currentPointerData.pointerEnter == null) {
for (var i = 0; i < currentPointerData.hovered.Count; ++i) {
EventExecutor.Execute(currentPointerData.hovered[i], currentPointerData, ExecuteEvents.pointerExitHandler);
}
currentPointerData.hovered.Clear();
if (newEnterTarget == null) {
currentPointerData.pointerEnter = newEnterTarget;
return;
}
}
// If we have not changed hover target.
if (newEnterTarget && currentPointerData.pointerEnter == newEnterTarget) {
return;
}
GameObject commonRoot = ModuleController.FindCommonRoot(currentPointerData.pointerEnter, newEnterTarget);
// We already an entered object from last time.
if (currentPointerData.pointerEnter != null) {
// Send exit handler call to all elements in the chain
// until we reach the new target, or null!
Transform t = currentPointerData.pointerEnter.transform;
while (t != null) {
// If we reach the common root break out!
if (commonRoot != null && commonRoot.transform == t)
break;
EventExecutor.Execute(t.gameObject, currentPointerData, ExecuteEvents.pointerExitHandler);
currentPointerData.hovered.Remove(t.gameObject);
t = t.parent;
}
}
// Now issue the enter call up to but not including the common root.
currentPointerData.pointerEnter = newEnterTarget;
if (newEnterTarget != null) {
Transform t = newEnterTarget.transform;
while (t != null && t.gameObject != commonRoot) {
EventExecutor.Execute(t.gameObject, currentPointerData, ExecuteEvents.pointerEnterHandler);
currentPointerData.hovered.Add(t.gameObject);
t = t.parent;
}
}
}
private void TryExitPointer() {
if (Pointer == null) {
return;
}
GameObject currentGameObject = GetCurrentGameObject();
if (currentGameObject) {
Pointer.OnPointerExit(currentGameObject);
}
}
private bool IsPointerActiveAndAvailable() {
return pointer != null && pointer.IsAvailable;
}
private void RaycastAll() {
ModuleController.RaycastResultCache.Clear();
ModuleController.eventSystem.RaycastAll(CurrentEventData, ModuleController.RaycastResultCache);
}
}

View File

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

View File

@@ -0,0 +1,32 @@
// 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.EventSystems;
/// Provides an interface for executing events for _IEventSystemHandler_.
public interface IGvrEventExecutor {
bool Execute<T>(GameObject target,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> functor)
where T : IEventSystemHandler;
GameObject ExecuteHierarchy<T>(GameObject root,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> callbackFunction)
where T : IEventSystemHandler;
GameObject GetEventHandler<T>(GameObject root)
where T : IEventSystemHandler;
}

View File

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

View File

@@ -0,0 +1,30 @@
// 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;
using UnityEngine.EventSystems;
/// Interface for manipulating an InputModule used by _GvrPointerInputModuleImpl_
public interface IGvrInputModuleController {
EventSystem eventSystem { get; }
List<RaycastResult> RaycastResultCache { get; }
bool ShouldActivate();
void Deactivate();
GameObject FindCommonRoot(GameObject g1, GameObject g2);
BaseEventData GetBaseEventData();
RaycastResult FindFirstRaycast(List<RaycastResult> candidates);
}

View File

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

View File

@@ -0,0 +1,64 @@
// 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.Runtime.InteropServices;
// General Cardboard helper methods.
public class GvrCardboardHelpers {
/// Manual recenter for Cardboard apps. After recentering the camera's orientation will be given
/// in the new recentered coordinate system.
/// Do not use for Daydream apps as controller based recentering is handled automatically by
/// Google VR Services, see `GvrControllerInput.Rencentered` for details.
public static void Recenter() {
#if UNITY_EDITOR
if (GvrEditorEmulator.Instance != null) {
GvrEditorEmulator.Instance.Recenter();
}
#elif (UNITY_ANDROID || UNITY_IOS)
IntPtr gvrContextPtr = GvrSettings.GetValidGvrNativePtrOrLogError();
if (gvrContextPtr == IntPtr.Zero) {
return;
}
gvr_reset_tracking(gvrContextPtr);
#endif // (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
Debug.Log("Use GvrEditorEmulator for in-editor recentering");
}
/// Set the Cardboard viewer params.
/// Example URI for 2015 Cardboard Viewer V2:
/// http://google.com/cardboard/cfg?p=CgZHb29nbGUSEkNhcmRib2FyZCBJL08gMjAxNR0rGBU9JQHegj0qEAAASEIAAEhCAABIQgAASEJYADUpXA89OggeZnc-Ej6aPlAAYAM
public static void SetViewerProfile(String viewerProfileUri) {
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
IntPtr gvrContextPtr = GvrSettings.GetValidGvrNativePtrOrLogError();
if (gvrContextPtr == IntPtr.Zero) {
return;
}
gvr_set_default_viewer_profile(gvrContextPtr, viewerProfileUri);
#endif // (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
Debug.Log("Unavailable for non-Android and non-iOS builds");
}
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
[DllImport(GvrActivityHelper.GVR_DLL_NAME)]
private static extern void gvr_reset_tracking(IntPtr gvr_context);
[DllImport(GvrActivityHelper.GVR_DLL_NAME)]
private static extern void gvr_set_default_viewer_profile(IntPtr gvr_context,
[MarshalAs(UnmanagedType.LPStr)] string viewer_profile_uri);
#endif // (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
}

Some files were not shown because too many files have changed in this diff Show More