Added VR libraries
This commit is contained in:
8
Assets/GoogleVR/Scripts/Cardboard.meta
Normal file
8
Assets/GoogleVR/Scripts/Cardboard.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91877f3f2369205449e99f23398ff1d0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
219
Assets/GoogleVR/Scripts/Cardboard/GvrReticlePointer.cs
Normal file
219
Assets/GoogleVR/Scripts/Cardboard/GvrReticlePointer.cs
Normal 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
|
||||
}
|
||||
}
|
||||
12
Assets/GoogleVR/Scripts/Cardboard/GvrReticlePointer.cs.meta
Normal file
12
Assets/GoogleVR/Scripts/Cardboard/GvrReticlePointer.cs.meta
Normal 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:
|
||||
8
Assets/GoogleVR/Scripts/Controller.meta
Normal file
8
Assets/GoogleVR/Scripts/Controller.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 994be461bb83b934cbc35416f0dfae24
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GoogleVR/Scripts/Controller/ArmModel.meta
Normal file
8
Assets/GoogleVR/Scripts/Controller/ArmModel.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 86ebdf1723c09ee499974202114ef39d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
432
Assets/GoogleVR/Scripts/Controller/ArmModel/GvrArmModel.cs
Normal file
432
Assets/GoogleVR/Scripts/Controller/ArmModel/GvrArmModel.cs
Normal file
@@ -0,0 +1,432 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
/// Standard implementation for a mathematical model to make the virtual controller approximate the
|
||||
/// physical location of the Daydream controller.
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrArmModel")]
|
||||
public class GvrArmModel : GvrBaseArmModel, IGvrControllerInputDeviceReceiver {
|
||||
/// Position of the elbow joint relative to the head before the arm model is applied.
|
||||
public Vector3 elbowRestPosition = DEFAULT_ELBOW_REST_POSITION;
|
||||
|
||||
/// Position of the wrist joint relative to the elbow before the arm model is applied.
|
||||
public Vector3 wristRestPosition = DEFAULT_WRIST_REST_POSITION;
|
||||
|
||||
/// Position of the controller joint relative to the wrist before the arm model is applied.
|
||||
public Vector3 controllerRestPosition = DEFAULT_CONTROLLER_REST_POSITION;
|
||||
|
||||
/// Offset applied to the elbow position as the controller is rotated upwards.
|
||||
public Vector3 armExtensionOffset = DEFAULT_ARM_EXTENSION_OFFSET;
|
||||
|
||||
/// Ratio of the controller's rotation to apply to the rotation of the elbow.
|
||||
/// The remaining rotation is applied to the wrist's rotation.
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float elbowBendRatio = DEFAULT_ELBOW_BEND_RATIO;
|
||||
|
||||
/// Offset in front of the controller to determine what position to use when determing if the
|
||||
/// controller should fade. This is useful when objects are attached to the controller.
|
||||
[Range(0.0f, 0.4f)]
|
||||
public float fadeControllerOffset = 0.0f;
|
||||
|
||||
/// Controller distance from the front/back of the head after which the controller disappears (meters).
|
||||
[Range(0.0f, 0.4f)]
|
||||
public float fadeDistanceFromHeadForward = 0.25f;
|
||||
|
||||
/// Controller distance from the left/right of the head after which the controller disappears (meters).
|
||||
[Range(0.0f, 0.4f)]
|
||||
public float fadeDistanceFromHeadSide = 0.15f;
|
||||
|
||||
/// Controller distance from face after which the tooltips appear (meters).
|
||||
[Range(0.4f, 0.6f)]
|
||||
public float tooltipMinDistanceFromFace = 0.45f;
|
||||
|
||||
/// When the angle (degrees) between the controller and the head is larger than
|
||||
/// this value, the tooltips disappear.
|
||||
/// If the value is 180, then the tooltips are always shown.
|
||||
/// If the value is 90, the tooltips are only shown when they are facing the camera.
|
||||
[Range(0, 180)]
|
||||
public int tooltipMaxAngleFromCamera = 80;
|
||||
|
||||
/// If true, the root of the pose is locked to the local position of the player's neck.
|
||||
public bool isLockedToNeck = false;
|
||||
|
||||
/// Represents the controller's position relative to the user's head.
|
||||
public override Vector3 ControllerPositionFromHead {
|
||||
get {
|
||||
return controllerPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the controller's rotation relative to the user's head.
|
||||
public override Quaternion ControllerRotationFromHead {
|
||||
get {
|
||||
return controllerRotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// The suggested rendering alpha value of the controller.
|
||||
/// This is to prevent the controller from intersecting the face.
|
||||
/// The range is always 0 - 1.
|
||||
public override float PreferredAlpha {
|
||||
get {
|
||||
return preferredAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
/// The suggested rendering alpha value of the controller tooltips.
|
||||
/// This is to only display the tooltips when the player is looking
|
||||
/// at the controller, and also to prevent the tooltips from intersecting the
|
||||
/// player's face.
|
||||
public override float TooltipAlphaValue {
|
||||
get {
|
||||
return tooltipAlphaValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the neck's position relative to the user's head.
|
||||
/// If isLockedToNeck is true, this will be the InputTracking position of the Head node modified
|
||||
/// by an inverse neck model to approximate the neck position.
|
||||
/// Otherwise, it is always zero.
|
||||
public Vector3 NeckPosition {
|
||||
get {
|
||||
return neckPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the shoulder's position relative to the user's head.
|
||||
/// This is not actually used as part of the arm model calculations, and exists for debugging.
|
||||
public Vector3 ShoulderPosition {
|
||||
get {
|
||||
Vector3 shoulderPosition = neckPosition + torsoRotation * Vector3.Scale(SHOULDER_POSITION, handedMultiplier);
|
||||
return shoulderPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the shoulder's rotation relative to the user's head.
|
||||
/// This is not actually used as part of the arm model calculations, and exists for debugging.
|
||||
public Quaternion ShoulderRotation {
|
||||
get {
|
||||
return torsoRotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the elbow's position relative to the user's head.
|
||||
public Vector3 ElbowPosition {
|
||||
get {
|
||||
return elbowPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the elbow's rotation relative to the user's head.
|
||||
public Quaternion ElbowRotation {
|
||||
get {
|
||||
return elbowRotation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the wrist's position relative to the user's head.
|
||||
public Vector3 WristPosition {
|
||||
get {
|
||||
return wristPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the wrist's rotation relative to the user's head.
|
||||
public Quaternion WristRotation {
|
||||
get {
|
||||
return wristRotation;
|
||||
}
|
||||
}
|
||||
|
||||
public GvrControllerInputDevice ControllerInputDevice { get; set; }
|
||||
|
||||
protected Vector3 neckPosition;
|
||||
protected Vector3 elbowPosition;
|
||||
protected Quaternion elbowRotation;
|
||||
protected Vector3 wristPosition;
|
||||
protected Quaternion wristRotation;
|
||||
protected Vector3 controllerPosition;
|
||||
protected Quaternion controllerRotation;
|
||||
protected float preferredAlpha;
|
||||
protected float tooltipAlphaValue;
|
||||
|
||||
/// Multiplier for handedness such that 1 = Right, 0 = Center, -1 = left.
|
||||
protected Vector3 handedMultiplier;
|
||||
|
||||
/// Forward direction of user's torso.
|
||||
protected Vector3 torsoDirection;
|
||||
|
||||
/// Orientation of the user's torso.
|
||||
protected Quaternion torsoRotation;
|
||||
|
||||
// Default values for tuning variables.
|
||||
public static readonly Vector3 DEFAULT_ELBOW_REST_POSITION = new Vector3(0.195f, -0.5f, 0.005f);
|
||||
public static readonly Vector3 DEFAULT_WRIST_REST_POSITION = new Vector3(0.0f, 0.0f, 0.25f);
|
||||
public static readonly Vector3 DEFAULT_CONTROLLER_REST_POSITION = new Vector3(0.0f, 0.0f, 0.05f);
|
||||
public static readonly Vector3 DEFAULT_ARM_EXTENSION_OFFSET = new Vector3(-0.13f, 0.14f, 0.08f);
|
||||
public const float DEFAULT_ELBOW_BEND_RATIO = 0.6f;
|
||||
|
||||
/// Increases elbow bending as the controller moves up (unitless).
|
||||
protected const float EXTENSION_WEIGHT = 0.4f;
|
||||
|
||||
/// Rest position for shoulder joint.
|
||||
protected static readonly Vector3 SHOULDER_POSITION = new Vector3(0.17f, -0.2f, -0.03f);
|
||||
|
||||
/// Neck offset used to apply the inverse neck model when locked to the head.
|
||||
protected static readonly Vector3 NECK_OFFSET = new Vector3(0.0f, 0.075f, 0.08f);
|
||||
|
||||
/// Amount of normalized alpha transparency to change per second.
|
||||
protected const float DELTA_ALPHA = 4.0f;
|
||||
|
||||
/// Angle ranges the for arm extension offset to start and end (degrees).
|
||||
protected const float MIN_EXTENSION_ANGLE = 7.0f;
|
||||
protected const float MAX_EXTENSION_ANGLE = 60.0f;
|
||||
|
||||
protected virtual void OnEnable() {
|
||||
// Register the controller update listener.
|
||||
GvrControllerInput.OnControllerInputUpdated += OnControllerInputUpdated;
|
||||
|
||||
// Force the torso direction to match the gaze direction immediately.
|
||||
// Otherwise, the controller will not be positioned correctly if the ArmModel was enabled
|
||||
// when the user wasn't facing forward.
|
||||
UpdateTorsoDirection(true);
|
||||
|
||||
// Update immediately to avoid a frame delay before the arm model is applied.
|
||||
OnControllerInputUpdated();
|
||||
}
|
||||
|
||||
protected virtual void OnDisable() {
|
||||
GvrControllerInput.OnControllerInputUpdated -= OnControllerInputUpdated;
|
||||
}
|
||||
|
||||
protected virtual void OnControllerInputUpdated() {
|
||||
UpdateHandedness();
|
||||
UpdateTorsoDirection(false);
|
||||
UpdateNeckPosition();
|
||||
ApplyArmModel();
|
||||
UpdateTransparency();
|
||||
}
|
||||
|
||||
protected virtual void UpdateHandedness() {
|
||||
// Update user handedness if the setting has changed.
|
||||
if (ControllerInputDevice == null) {
|
||||
return;
|
||||
}
|
||||
// Determine handedness multiplier.
|
||||
handedMultiplier.Set(0, 1, 1);
|
||||
if (ControllerInputDevice.IsRightHand) {
|
||||
handedMultiplier.x = 1.0f;
|
||||
} else {
|
||||
handedMultiplier.x = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UpdateTorsoDirection(bool forceImmediate) {
|
||||
// Determine the gaze direction horizontally.
|
||||
Vector3 gazeDirection = GvrVRHelpers.GetHeadForward();
|
||||
gazeDirection.y = 0.0f;
|
||||
gazeDirection.Normalize();
|
||||
|
||||
// Use the gaze direction to update the forward direction.
|
||||
if (forceImmediate ||
|
||||
(ControllerInputDevice != null && ControllerInputDevice.Recentered)) {
|
||||
torsoDirection = gazeDirection;
|
||||
} else {
|
||||
float angularVelocity = ControllerInputDevice != null ? ControllerInputDevice.Gyro.magnitude : 0;
|
||||
float gazeFilterStrength = Mathf.Clamp((angularVelocity - 0.2f) / 45.0f, 0.0f, 0.1f);
|
||||
torsoDirection = Vector3.Slerp(torsoDirection, gazeDirection, gazeFilterStrength);
|
||||
}
|
||||
|
||||
// Calculate the torso rotation.
|
||||
torsoRotation = Quaternion.FromToRotation(Vector3.forward, torsoDirection);
|
||||
}
|
||||
|
||||
protected virtual void UpdateNeckPosition() {
|
||||
if (isLockedToNeck) {
|
||||
// Returns the center of the eyes.
|
||||
// However, we actually want to lock to the center of the head.
|
||||
neckPosition = GvrVRHelpers.GetHeadPosition();
|
||||
|
||||
// Find the approximate neck position by Applying an inverse neck model.
|
||||
// This transforms the head position to the center of the head and also accounts
|
||||
// for the head's rotation so that the motion feels more natural.
|
||||
neckPosition = ApplyInverseNeckModel(neckPosition);
|
||||
} else {
|
||||
neckPosition = Vector3.zero;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ApplyArmModel() {
|
||||
// Set the starting positions of the joints before they are transformed by the arm model.
|
||||
SetUntransformedJointPositions();
|
||||
|
||||
// Get the controller's orientation.
|
||||
Quaternion controllerOrientation;
|
||||
Quaternion xyRotation;
|
||||
float xAngle;
|
||||
GetControllerRotation(out controllerOrientation, out xyRotation, out xAngle);
|
||||
|
||||
// Offset the elbow by the extension offset.
|
||||
float extensionRatio = CalculateExtensionRatio(xAngle);
|
||||
ApplyExtensionOffset(extensionRatio);
|
||||
|
||||
// Calculate the lerp rotation, which is used to control how much the rotation of the
|
||||
// controller impacts each joint.
|
||||
Quaternion lerpRotation = CalculateLerpRotation(xyRotation, extensionRatio);
|
||||
|
||||
CalculateFinalJointRotations(controllerOrientation, xyRotation, lerpRotation);
|
||||
ApplyRotationToJoints();
|
||||
}
|
||||
|
||||
/// Set the starting positions of the joints before they are transformed by the arm model.
|
||||
protected virtual void SetUntransformedJointPositions() {
|
||||
elbowPosition = Vector3.Scale(elbowRestPosition, handedMultiplier);
|
||||
wristPosition = Vector3.Scale(wristRestPosition, handedMultiplier);
|
||||
controllerPosition = Vector3.Scale(controllerRestPosition, handedMultiplier);
|
||||
}
|
||||
|
||||
/// Calculate the extension ratio based on the angle of the controller along the x axis.
|
||||
protected virtual float CalculateExtensionRatio(float xAngle) {
|
||||
float normalizedAngle = (xAngle - MIN_EXTENSION_ANGLE) / (MAX_EXTENSION_ANGLE - MIN_EXTENSION_ANGLE);
|
||||
float extensionRatio = Mathf.Clamp(normalizedAngle, 0.0f, 1.0f);
|
||||
return extensionRatio;
|
||||
}
|
||||
|
||||
/// Offset the elbow by the extension offset.
|
||||
protected virtual void ApplyExtensionOffset(float extensionRatio) {
|
||||
Vector3 extensionOffset = Vector3.Scale(armExtensionOffset, handedMultiplier);
|
||||
elbowPosition += extensionOffset * extensionRatio;
|
||||
}
|
||||
|
||||
/// Calculate the lerp rotation, which is used to control how much the rotation of the
|
||||
/// controller impacts each joint.
|
||||
protected virtual Quaternion CalculateLerpRotation(Quaternion xyRotation, float extensionRatio) {
|
||||
float totalAngle = Quaternion.Angle(xyRotation, Quaternion.identity);
|
||||
float lerpSuppresion = 1.0f - Mathf.Pow(totalAngle / 180.0f, 6.0f);
|
||||
float inverseElbowBendRatio = 1.0f - elbowBendRatio;
|
||||
float lerpValue = inverseElbowBendRatio + elbowBendRatio * extensionRatio * EXTENSION_WEIGHT;
|
||||
lerpValue *= lerpSuppresion;
|
||||
return Quaternion.Lerp(Quaternion.identity, xyRotation, lerpValue);
|
||||
}
|
||||
|
||||
/// Determine the final joint rotations relative to the head.
|
||||
protected virtual void CalculateFinalJointRotations(Quaternion controllerOrientation, Quaternion xyRotation, Quaternion lerpRotation) {
|
||||
elbowRotation = torsoRotation * Quaternion.Inverse(lerpRotation) * xyRotation;
|
||||
wristRotation = elbowRotation * lerpRotation;
|
||||
controllerRotation = torsoRotation * controllerOrientation;
|
||||
}
|
||||
|
||||
/// Apply the joint rotations to the positions of the joints to determine the final pose.
|
||||
protected virtual void ApplyRotationToJoints() {
|
||||
elbowPosition = neckPosition + torsoRotation * elbowPosition;
|
||||
wristPosition = elbowPosition + elbowRotation * wristPosition;
|
||||
controllerPosition = wristPosition + wristRotation * controllerPosition;
|
||||
}
|
||||
|
||||
/// Transform the head position into an approximate neck position.
|
||||
protected virtual Vector3 ApplyInverseNeckModel(Vector3 headPosition) {
|
||||
Quaternion headRotation = GvrVRHelpers.GetHeadRotation();
|
||||
Vector3 rotatedNeckOffset =
|
||||
headRotation * NECK_OFFSET - NECK_OFFSET.y * Vector3.up;
|
||||
headPosition -= rotatedNeckOffset;
|
||||
|
||||
return headPosition;
|
||||
}
|
||||
|
||||
/// Controls the transparency of the controller to prevent the controller from clipping through
|
||||
/// the user's head. Also, controls the transparency of the tooltips so they are only visible
|
||||
/// when the controller is held up.
|
||||
protected virtual void UpdateTransparency() {
|
||||
Vector3 controllerForward = controllerRotation * Vector3.forward;
|
||||
Vector3 offsetControllerPosition = controllerPosition + (controllerForward * fadeControllerOffset);
|
||||
Vector3 controllerRelativeToHead = offsetControllerPosition - neckPosition;
|
||||
|
||||
Vector3 headForward = GvrVRHelpers.GetHeadForward();
|
||||
float distanceToHeadForward = Vector3.Scale(controllerRelativeToHead, headForward).magnitude;
|
||||
Vector3 headRight = Vector3.Cross(headForward, Vector3.up);
|
||||
float distanceToHeadSide = Vector3.Scale(controllerRelativeToHead, headRight).magnitude;
|
||||
float distanceToHeadUp = Mathf.Abs(controllerRelativeToHead.y);
|
||||
|
||||
bool shouldFadeController = distanceToHeadForward < fadeDistanceFromHeadForward
|
||||
&& distanceToHeadUp < fadeDistanceFromHeadForward
|
||||
&& distanceToHeadSide < fadeDistanceFromHeadSide;
|
||||
|
||||
// Determine how vertical the controller is pointing.
|
||||
float animationDelta = DELTA_ALPHA * Time.unscaledDeltaTime;
|
||||
if (shouldFadeController) {
|
||||
preferredAlpha = Mathf.Max(0.0f, preferredAlpha - animationDelta);
|
||||
} else {
|
||||
preferredAlpha = Mathf.Min(1.0f, preferredAlpha + animationDelta);
|
||||
}
|
||||
|
||||
float dot = Vector3.Dot(controllerRotation * Vector3.up, -controllerRelativeToHead.normalized);
|
||||
float minDot = (tooltipMaxAngleFromCamera - 90.0f) / -90.0f;
|
||||
float distToFace = Vector3.Distance(controllerRelativeToHead, Vector3.zero);
|
||||
if (shouldFadeController
|
||||
|| distToFace > tooltipMinDistanceFromFace
|
||||
|| dot < minDot) {
|
||||
tooltipAlphaValue = Mathf.Max(0.0f, tooltipAlphaValue - animationDelta);
|
||||
} else {
|
||||
tooltipAlphaValue = Mathf.Min(1.0f, tooltipAlphaValue + animationDelta);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the controller's orientation.
|
||||
protected void GetControllerRotation(out Quaternion rotation, out Quaternion xyRotation, out float xAngle) {
|
||||
// Find the controller's orientation relative to the player.
|
||||
rotation = ControllerInputDevice != null ? ControllerInputDevice.Orientation : Quaternion.identity;
|
||||
rotation = Quaternion.Inverse(torsoRotation) * rotation;
|
||||
|
||||
// Extract just the x rotation angle.
|
||||
Vector3 controllerForward = rotation * Vector3.forward;
|
||||
xAngle = 90.0f - Vector3.Angle(controllerForward, Vector3.up);
|
||||
|
||||
// Remove the z rotation from the controller.
|
||||
xyRotation = Quaternion.FromToRotation(Vector3.forward, controllerForward);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected virtual void OnDrawGizmosSelected() {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (transform.parent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 worldShoulder = transform.parent.TransformPoint(ShoulderPosition);
|
||||
Vector3 worldElbow = transform.parent.TransformPoint(elbowPosition);
|
||||
Vector3 worldwrist = transform.parent.TransformPoint(wristPosition);
|
||||
Vector3 worldcontroller = transform.parent.TransformPoint(controllerPosition);
|
||||
|
||||
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawSphere(worldShoulder, 0.02f);
|
||||
Gizmos.DrawLine(worldShoulder, worldElbow);
|
||||
|
||||
Gizmos.color = Color.green;
|
||||
Gizmos.DrawSphere(worldElbow, 0.02f);
|
||||
Gizmos.DrawLine(worldElbow, worldwrist);
|
||||
|
||||
Gizmos.color = Color.cyan;
|
||||
Gizmos.DrawSphere(worldwrist, 0.02f);
|
||||
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawSphere(worldcontroller, 0.02f);
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7dda4bb2d5509e44e978d2bf56e25d7c
|
||||
timeCreated: 1471566115
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
/// Interface for a mathematical model that uses the orientation and location
|
||||
/// of the physical controller, and predicts the location of the controller and pointer
|
||||
/// to determine where to place the controller model within the scene.
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrBaseArmModel")]
|
||||
public abstract class GvrBaseArmModel : MonoBehaviour {
|
||||
/// Vector to represent the controller's location relative to
|
||||
/// the user's head position.
|
||||
public abstract Vector3 ControllerPositionFromHead { get; }
|
||||
|
||||
/// Quaternion to represent the controller's rotation relative to
|
||||
/// the user's head position.
|
||||
public abstract Quaternion ControllerRotationFromHead { get; }
|
||||
|
||||
/// The suggested rendering alpha value of the controller.
|
||||
/// This is to prevent the controller from intersecting the face.
|
||||
/// The range is always 0 - 1.
|
||||
public abstract float PreferredAlpha { get; }
|
||||
|
||||
/// The suggested rendering alpha value of the controller tooltips.
|
||||
/// This is to only display the tooltips when the player is looking
|
||||
/// at the controller, and also to prevent the tooltips from intersecting the
|
||||
/// player's face.
|
||||
public abstract float TooltipAlphaValue { get; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b621d217eade547b4841c4471106b6e5
|
||||
timeCreated: 1495573504
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
public interface IGvrArmModelReceiver {
|
||||
GvrBaseArmModel ArmModel { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb01f40fa958548a5b1f92a685e6d46d
|
||||
timeCreated: 1495576213
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
630
Assets/GoogleVR/Scripts/Controller/GvrControllerInput.cs
Normal file
630
Assets/GoogleVR/Scripts/Controller/GvrControllerInput.cs
Normal file
@@ -0,0 +1,630 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Gvr.Internal;
|
||||
|
||||
/// Represents a controller's current connection state.
|
||||
/// All values and semantics below (except for Error) are
|
||||
/// from gvr_types.h in the GVR C API.
|
||||
public enum GvrConnectionState {
|
||||
/// Indicates that an error has occurred.
|
||||
Error = -1,
|
||||
|
||||
/// Indicates a controller is disconnected.
|
||||
Disconnected = 0,
|
||||
/// Indicates that the device is scanning for controllers.
|
||||
Scanning = 1,
|
||||
/// Indicates that the device is connecting to a controller.
|
||||
Connecting = 2,
|
||||
/// Indicates that the device is connected to a controller.
|
||||
Connected = 3,
|
||||
}
|
||||
|
||||
/// Represents the status of the controller API.
|
||||
/// Values and semantics from gvr_types.h in the GVR C API.
|
||||
public enum GvrControllerApiStatus {
|
||||
/// A Unity-localized error occurred.
|
||||
/// This is the only value that isn't in gvr_types.h.
|
||||
Error = -1,
|
||||
|
||||
/// API is happy and healthy. This doesn't mean any controllers are
|
||||
/// connected, it just means that the underlying service is working
|
||||
/// properly.
|
||||
Ok = 0,
|
||||
|
||||
/// Any other status represents a permanent failure that requires
|
||||
/// external action to fix:
|
||||
|
||||
/// API failed because this device does not support controllers (API is too
|
||||
/// low, or other required feature not present).
|
||||
Unsupported = 1,
|
||||
/// This app was not authorized to use the service (e.g., missing permissions,
|
||||
/// the app is blacklisted by the underlying service, etc).
|
||||
NotAuthorized = 2,
|
||||
/// The underlying VR service is not present.
|
||||
Unavailable = 3,
|
||||
/// The underlying VR service is too old, needs upgrade.
|
||||
ApiServiceObsolete = 4,
|
||||
/// The underlying VR service is too new, is incompatible with current client.
|
||||
ApiClientObsolete = 5,
|
||||
/// The underlying VR service is malfunctioning. Try again later.
|
||||
ApiMalfunction = 6,
|
||||
}
|
||||
|
||||
/// Represents a controller's current battery level.
|
||||
/// Values and semantics from gvr_types.h in the GVR C API.
|
||||
public enum GvrControllerBatteryLevel {
|
||||
/// A Unity-localized error occurred.
|
||||
/// This is the only value that isn't in gvr_types.h.
|
||||
Error = -1,
|
||||
|
||||
/// The battery state is currently unreported.
|
||||
Unknown = 0,
|
||||
|
||||
/// Equivalent to 1 out of 5 bars on the battery indicator.
|
||||
CriticalLow = 1,
|
||||
|
||||
/// Equivalent to 2 out of 5 bars on the battery indicator.
|
||||
Low = 2,
|
||||
|
||||
/// Equivalent to 3 out of 5 bars on the battery indicator.
|
||||
Medium = 3,
|
||||
|
||||
/// Equivalent to 4 out of 5 bars on the battery indicator.
|
||||
AlmostFull = 4,
|
||||
|
||||
/// Equivalent to 5 out of 5 bars on the battery indicator.
|
||||
Full = 5,
|
||||
}
|
||||
|
||||
/// Represents controller buttons.
|
||||
/// Values 0-9 from gvr_types.h in the GVR C API.
|
||||
/// Value 31 not represented in the C API.
|
||||
public enum GvrControllerButton {
|
||||
/// Button under the touch pad. Formerly known as Click.
|
||||
TouchPadButton = 1 << 1,
|
||||
|
||||
/// Touch pad touching indicator.
|
||||
TouchPadTouch = 1 << 31,
|
||||
|
||||
/// General application button.
|
||||
App = 1 << 3,
|
||||
|
||||
/// System button. Formerly known as Home.
|
||||
System = 1 << 2,
|
||||
|
||||
/// Buttons reserved for future use. Subject to name change.
|
||||
Reserved0 = 1 << 6,
|
||||
Reserved1 = 1 << 7,
|
||||
Reserved2 = 1 << 8,
|
||||
|
||||
}
|
||||
|
||||
/// Represents controller handedness.
|
||||
public enum GvrControllerHand {
|
||||
Right,
|
||||
Left,
|
||||
Dominant, // Alias for dominant hand as specified by `GvrSettings.Handedness`.
|
||||
NonDominant, // Alias for non-dominant hand.
|
||||
}
|
||||
|
||||
|
||||
/// Main entry point for the Daydream controller API.
|
||||
///
|
||||
/// To use this API, add this script to a game object in your scene, or use the
|
||||
/// **GvrControllerMain** prefab.
|
||||
///
|
||||
/// This is a singleton object. There can only be one object with this script in your scene.
|
||||
///
|
||||
/// To access a controller's state, get a device from `GvrControllerInput.GetDevice` then
|
||||
/// query it for state. For example, to the dominant controller's current orientation, use
|
||||
/// `GvrControllerInput.GetDevice(GvrControllerHand.Dominant).Orientation`.
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerInput")]
|
||||
public class GvrControllerInput : MonoBehaviour {
|
||||
private static GvrControllerInputDevice[] instances = new GvrControllerInputDevice[0];
|
||||
private static IControllerProvider controllerProvider;
|
||||
private static GvrSettings.UserPrefsHandedness handedness;
|
||||
private static Action onDevicesChangedInternal;
|
||||
|
||||
/// Event handler for receiving button, touchpad, and IMU updates from the controllers.
|
||||
/// Use this handler to update app state based on controller input.
|
||||
public static event Action OnControllerInputUpdated;
|
||||
|
||||
/// Event handler for receiving a second notification callback, after all
|
||||
/// `OnControllerInputUpdated` events have fired.
|
||||
public static event Action OnPostControllerInputUpdated;
|
||||
|
||||
/// Event handler for when the connection state of a controller changes.
|
||||
public delegate void OnStateChangedEvent(GvrConnectionState state, GvrConnectionState oldState);
|
||||
|
||||
/// Event handler for when controller devices have changed. Any code that stores a
|
||||
/// `GvrControllerInputDevice` should get a new device instance from `GetDevice`.
|
||||
/// Existing `GvrControllerInputDevice`s will be marked invalid and will log errors
|
||||
/// when used. Event handlers are called immediately when added.
|
||||
public static event Action OnDevicesChanged {
|
||||
add {
|
||||
onDevicesChangedInternal += value;
|
||||
value();
|
||||
}
|
||||
remove {
|
||||
onDevicesChangedInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Event handler for when the connection state of the dominant controller changes.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.OnStateChangedEvent.")]
|
||||
public static event OnStateChangedEvent OnStateChanged {
|
||||
add {
|
||||
if (instances.Length > 0) {
|
||||
instances[0].OnStateChanged += value;
|
||||
} else {
|
||||
Debug.LogError("GvrControllerInput: Adding OnStateChanged event before instance created.");
|
||||
}
|
||||
}
|
||||
remove {
|
||||
if (instances.Length > 0) {
|
||||
instances[0].OnStateChanged -= value;
|
||||
} else {
|
||||
Debug.LogError("GvrControllerInput: Removing OnStateChanged event before instance created.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum EmulatorConnectionMode {
|
||||
OFF,
|
||||
USB,
|
||||
WIFI,
|
||||
}
|
||||
/// Indicates how we connect to the controller emulator.
|
||||
[GvrInfo("Hold Shift to use the Mouse as the dominant controller.\n\n" +
|
||||
"Controls: Shift +\n" +
|
||||
" • Move Mouse = Change Orientation\n" +
|
||||
" • Left Mouse Button = ClickButton\n" +
|
||||
" • Right Mouse Button = AppButton\n" +
|
||||
" • Middle Mouse Button = HomeButton/Recenter\n" +
|
||||
" • Ctrl = IsTouching\n" +
|
||||
" • Ctrl + Move Mouse = Change TouchPos", 8)]
|
||||
[Tooltip("How to connect to the emulator: USB cable (recommended) or WIFI.")]
|
||||
|
||||
public EmulatorConnectionMode emulatorConnectionMode = EmulatorConnectionMode.USB;
|
||||
|
||||
/// Returns a controller device for the specified hand.
|
||||
public static GvrControllerInputDevice GetDevice(GvrControllerHand hand) {
|
||||
if (instances.Length == 0) {
|
||||
return null;
|
||||
}
|
||||
// Remap Right and Left to Dominant or NonDominant according to settings handedness.
|
||||
if (hand == GvrControllerHand.Left || hand == GvrControllerHand.Right) {
|
||||
if ((int)hand != (int)handedness) {
|
||||
hand = GvrControllerHand.NonDominant;
|
||||
} else {
|
||||
hand = GvrControllerHand.Dominant;
|
||||
}
|
||||
}
|
||||
|
||||
if (hand == GvrControllerHand.NonDominant) {
|
||||
return instances[1];
|
||||
} else {
|
||||
// Dominant is always controller 0.
|
||||
return instances[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the dominant controller's current connection state. Returns
|
||||
/// `GvrConnectionState.Error` if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.State.")]
|
||||
public static GvrConnectionState State {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return GvrConnectionState.Error;
|
||||
}
|
||||
return instances[0].State;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the status of the controller API. Returns
|
||||
/// `GvrControllerApiStatus.Error` if `GvrControllerInput` is uninitialized.
|
||||
public static GvrControllerApiStatus ApiStatus {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return GvrControllerApiStatus.Error;
|
||||
}
|
||||
return instances[0].ApiStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if battery status is supported. Returns false if
|
||||
/// `GvrControllerInput` is uninitialized.
|
||||
public static bool SupportsBatteryStatus {
|
||||
get {
|
||||
if (controllerProvider == null) {
|
||||
return false;
|
||||
}
|
||||
return controllerProvider.SupportsBatteryStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the dominant controller's current orientation in space, as a quaternion.
|
||||
/// Returns `Quaternion.identity` if `GvrControllerInput` is uninitialized.
|
||||
/// The rotation is provided in 'orientation space' which means the rotation is given relative
|
||||
/// to the last time the user recentered their controllers. To make a game object in your scene
|
||||
/// have the same orientation as the dominant controller, simply assign this quaternion to the
|
||||
/// object's `transform.rotation`. To match the relative rotation, use `transform.localRotation`
|
||||
/// instead.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.Orientation.")]
|
||||
public static Quaternion Orientation {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return Quaternion.identity;
|
||||
}
|
||||
return instances[0].Orientation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the dominant controller's current angular speed in radians per second, using the right-hand
|
||||
/// rule (positive means a right-hand rotation about the given axis), as measured by the
|
||||
/// controller's gyroscope. Returns `Vector3.zero` if `GvrControllerInput` is uninitialized.
|
||||
/// The controller's axes are:
|
||||
/// - X points to the right,
|
||||
/// - Y points perpendicularly up from the controller's top surface
|
||||
/// - Z lies along the controller's body, pointing towards the front
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.Gyro.")]
|
||||
public static Vector3 Gyro {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return Vector3.zero;
|
||||
}
|
||||
return instances[0].Gyro;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the dominant controller's current acceleration in meters per second squared.
|
||||
/// Returns `Vector3.zero` if `GvrControllerInput` is uninitialized.
|
||||
/// The controller's axes are:
|
||||
/// - X points to the right,
|
||||
/// - Y points perpendicularly up from the controller's top surface
|
||||
/// - Z lies along the controller's body, pointing towards the front
|
||||
/// Note that gravity is indistinguishable from acceleration, so when the controller is resting
|
||||
/// on a surface, expect to measure an acceleration of 9.8 m/s^2 on the Y axis. The accelerometer
|
||||
/// reading will be zero on all three axes only if the controller is in free fall, or if the user
|
||||
/// is in a zero gravity environment like a space station.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.Accel.")]
|
||||
public static Vector3 Accel {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return Vector3.zero;
|
||||
}
|
||||
return instances[0].Accel;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true while the user is touching the dominant controller's touchpad. Returns
|
||||
/// false if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.TouchPadTouch).")]
|
||||
public static bool IsTouching {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButton(GvrControllerButton.TouchPadTouch);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true in the frame the user starts touching the dominant controller's touchpad.
|
||||
/// Returns false if `GvrControllerInput` is uninitialized.
|
||||
/// Every TouchDown event is guaranteed to be followed by exactly one TouchUp event in a
|
||||
/// later frame. Also, TouchDown and TouchUp will never both be true in the same frame.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.TouchPadTouch).")]
|
||||
public static bool TouchDown {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButtonDown(GvrControllerButton.TouchPadTouch);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true the frame after the user stops touching the dominant controller's touchpad.
|
||||
/// Returns false if `GvrControllerInput` is uninitialized.
|
||||
/// Every TouchUp event is guaranteed to be preceded by exactly one TouchDown event in an
|
||||
/// earlier frame. Also, TouchDown and TouchUp will never both be true in the same frame.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonUp(GvrControllerButton.TouchPadTouch).")]
|
||||
public static bool TouchUp {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButtonUp(GvrControllerButton.TouchPadTouch);
|
||||
}
|
||||
}
|
||||
|
||||
/// Position of the dominant controller's current touch, if touching the touchpad.
|
||||
/// Returns `Vector2(0.5f, 0.5f)` if `GvrControllerInput` is uninitialized.
|
||||
/// If not touching, this is the position of the last touch (when the finger left the touchpad).
|
||||
/// The X and Y range is from 0 to 1.
|
||||
/// (0, 0) is the top left of the touchpad and (1, 1) is the bottom right of the touchpad.
|
||||
[System.Obsolete("Obsolete. Migrate to the center-relative GvrControllerInputDevice.TouchPos.")]
|
||||
public static Vector2 TouchPos {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return new Vector2(0.5f,0.5f);
|
||||
}
|
||||
Vector2 touchPos = instances[0].TouchPos;
|
||||
touchPos.x = (touchPos.x / 2.0f) + 0.5f;
|
||||
touchPos.y = (-touchPos.y / 2.0f) + 0.5f;
|
||||
return touchPos;
|
||||
}
|
||||
}
|
||||
|
||||
/// Position of the dominant controller's current touch, if touching the touchpad.
|
||||
/// Returns `Vector2.zero` if `GvrControllerInput` is uninitialized.
|
||||
/// If not touching, this is the position of the last touch (when the finger left the touchpad).
|
||||
/// The X and Y range is from -1 to 1. (-.707,-.707) is bottom left, (.707,.707) is upper right.
|
||||
/// (0, 0) is the center of the touchpad.
|
||||
/// The magnitude of the touch vector is guaranteed to be <= 1.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.TouchPos.")]
|
||||
public static Vector2 TouchPosCentered {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return Vector2.zero;
|
||||
}
|
||||
return instances[0].TouchPos;
|
||||
}
|
||||
}
|
||||
|
||||
[System.Obsolete("Use Recentered to detect when user has completed the recenter gesture.")]
|
||||
public static bool Recentering {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the user just completed the recenter gesture. Returns false if
|
||||
/// `GvrControllerInput` is uninitialized. The headset and the dominant controller's
|
||||
/// orientation are now being reported in the new recentered coordinate system. This
|
||||
/// is an event flag (it is true for only one frame after the event happens, then
|
||||
/// reverts to false).
|
||||
public static bool Recentered {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].Recentered;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true while the user holds down the dominant controller's touchpad button.
|
||||
/// Returns false if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.TouchPadButton).")]
|
||||
public static bool ClickButton {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButton(GvrControllerButton.TouchPadButton);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true in the frame the user starts pressing down the dominant controller's
|
||||
/// touchpad button. Returns false if `GvrControllerInput` is uninitialized. Every
|
||||
/// ClickButtonDown event is guaranteed to be followed by exactly one ClickButtonUp
|
||||
/// event in a later frame. Also, ClickButtonDown and ClickButtonUp will never both be
|
||||
/// true in the same frame.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.TouchPadButton).")]
|
||||
public static bool ClickButtonDown {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButtonDown(GvrControllerButton.TouchPadButton);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true the frame after the user stops pressing down the dominant controller's
|
||||
/// touchpad button. Returns false if `GvrControllerInput` is uninitialized. Every
|
||||
/// ClickButtonUp event is guaranteed to be preceded by exactly one ClickButtonDown
|
||||
/// event in an earlier frame. Also, ClickButtonDown and ClickButtonUp will never both
|
||||
/// be true in the same frame.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonUp(GvrControllerButton.TouchPadButton).")]
|
||||
public static bool ClickButtonUp {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButtonUp(GvrControllerButton.TouchPadButton);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true while the user holds down the dominant controller's app button. Returns
|
||||
/// false if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.App).")]
|
||||
public static bool AppButton {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButton(GvrControllerButton.App);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true in the frame the user starts pressing down the dominant controller's app button.
|
||||
/// Returns false if `GvrControllerInput` is uninitialized. Every AppButtonDown event is
|
||||
/// guaranteed to be followed by exactly one AppButtonUp event in a later frame.
|
||||
/// Also, AppButtonDown and AppButtonUp will never both be true in the same frame.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.App).")]
|
||||
public static bool AppButtonDown {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButtonDown(GvrControllerButton.App);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true the frame after the user stops pressing down the dominant controller's app button.
|
||||
/// Returns false if `GvrControllerInput` is uninitialized. Every AppButtonUp event is guaranteed
|
||||
/// to be preceded by exactly one AppButtonDown event in an earlier frame. Also, AppButtonDown
|
||||
/// and AppButtonUp will never both be true in the same frame.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonUp(GvrControllerButton.App).")]
|
||||
public static bool AppButtonUp {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButtonUp(GvrControllerButton.App);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true in the frame the user starts pressing down the dominant controller's system button.
|
||||
/// Returns false if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButtonDown(GvrControllerButton.System).")]
|
||||
public static bool HomeButtonDown {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButtonDown(GvrControllerButton.System);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true while the user holds down the dominant controller's system button.
|
||||
/// Returns false if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.GetButton(GvrControllerButton.System).")]
|
||||
public static bool HomeButtonState {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].GetButton(GvrControllerButton.System);
|
||||
}
|
||||
}
|
||||
|
||||
/// If the dominant controller's state == GvrConnectionState.Error, this contains details about
|
||||
/// the error. If `GvrControllerInput` is uninitialized this returns an error string describing
|
||||
/// the uninitialized state.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.ErrorDetails.")]
|
||||
public static string ErrorDetails {
|
||||
get {
|
||||
if (instances.Length > 0) {
|
||||
return instances[0].ErrorDetails;
|
||||
} else {
|
||||
return "No GvrControllerInput initialized instance found in scene. It may be missing, or it might "
|
||||
+ "not have initialized yet.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the GVR C library controller state pointer (gvr_controller_state*) for the dominant
|
||||
/// controller. Returns `IntPtr.Zero` if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.StatePtr.")]
|
||||
public static IntPtr StatePtr {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
return instances[0].StatePtr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the dominant controller is currently being charged. Returns false if
|
||||
/// `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.IsCharging.")]
|
||||
public static bool IsCharging {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return false;
|
||||
}
|
||||
return instances[0].IsCharging;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the dominant controller's current battery charge level. Returns
|
||||
/// `GvrControllerBatteryLevel.Error` if `GvrControllerInput` is uninitialized.
|
||||
[System.Obsolete("Replaced by GvrControllerInputDevice.BatteryLevel.")]
|
||||
public static GvrControllerBatteryLevel BatteryLevel {
|
||||
get {
|
||||
if (instances.Length == 0) {
|
||||
return GvrControllerBatteryLevel.Error;
|
||||
}
|
||||
return instances[0].BatteryLevel;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake() {
|
||||
if (instances.Length > 0) {
|
||||
Debug.LogError("More than one active GvrControllerInput instance was found in your scene. "
|
||||
+ "Ensure that there is only one GvrControllerInput.");
|
||||
this.enabled = false;
|
||||
return;
|
||||
}
|
||||
if (controllerProvider == null) {
|
||||
controllerProvider = ControllerProviderFactory.CreateControllerProvider(this);
|
||||
}
|
||||
|
||||
handedness = GvrSettings.Handedness;
|
||||
int controllerCount = 2;
|
||||
instances = new GvrControllerInputDevice[controllerCount];
|
||||
for (int i=0; i<controllerCount; i++) {
|
||||
instances[i] = new GvrControllerInputDevice(controllerProvider, i);
|
||||
}
|
||||
if (onDevicesChangedInternal != null) {
|
||||
onDevicesChangedInternal();
|
||||
}
|
||||
|
||||
// Keep screen on here, since GvrControllerInput must be in any GVR scene in order to enable
|
||||
// controller capabilities.
|
||||
Screen.sleepTimeout = SleepTimeout.NeverSleep;
|
||||
}
|
||||
|
||||
void Update() {
|
||||
foreach (var instance in instances) {
|
||||
if (instance != null) {
|
||||
instance.Update();
|
||||
}
|
||||
}
|
||||
|
||||
if (OnControllerInputUpdated != null) {
|
||||
OnControllerInputUpdated();
|
||||
}
|
||||
|
||||
if (OnPostControllerInputUpdated != null) {
|
||||
OnPostControllerInputUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
foreach (var instance in instances) {
|
||||
// Ensure this device will error if used again.
|
||||
instance.Invalidate();
|
||||
}
|
||||
instances = new GvrControllerInputDevice[0];
|
||||
if (onDevicesChangedInternal != null) {
|
||||
onDevicesChangedInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void OnApplicationPause(bool paused) {
|
||||
if (null == controllerProvider) return;
|
||||
if (paused) {
|
||||
controllerProvider.OnPause();
|
||||
} else {
|
||||
handedness = GvrSettings.Handedness;
|
||||
controllerProvider.OnResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41251d5f89d5546bb9d8ba907686b71f
|
||||
timeCreated: 1495645608
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -31123
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
250
Assets/GoogleVR/Scripts/Controller/GvrControllerInputDevice.cs
Normal file
250
Assets/GoogleVR/Scripts/Controller/GvrControllerInputDevice.cs
Normal file
@@ -0,0 +1,250 @@
|
||||
// Copyright 2018 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Gvr.Internal;
|
||||
|
||||
/// Device instance of the Daydream controller API.
|
||||
public class GvrControllerInputDevice {
|
||||
private IControllerProvider controllerProvider;
|
||||
private int controllerId;
|
||||
|
||||
private ControllerState controllerState = new ControllerState();
|
||||
private Vector2 touchPosCentered = Vector2.zero;
|
||||
|
||||
private int lastUpdatedFrameCount = -1;
|
||||
private bool valid;
|
||||
|
||||
/// Event handler for when the connection state of the controller changes.
|
||||
public event GvrControllerInput.OnStateChangedEvent OnStateChanged;
|
||||
|
||||
internal GvrControllerInputDevice(IControllerProvider provider, int controller_id) {
|
||||
controllerProvider = provider;
|
||||
controllerId = controller_id;
|
||||
valid = true;
|
||||
}
|
||||
|
||||
internal void Invalidate() {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
public bool IsDominantHand {
|
||||
get {
|
||||
return controllerId == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRightHand {
|
||||
get {
|
||||
if (controllerId == 0) {
|
||||
return GvrSettings.Handedness == GvrSettings.UserPrefsHandedness.Right;
|
||||
} else {
|
||||
return GvrSettings.Handedness == GvrSettings.UserPrefsHandedness.Left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the controller's current connection state.
|
||||
public GvrConnectionState State {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.connectionState;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the API status of the current controller state.
|
||||
public GvrControllerApiStatus ApiStatus {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.apiStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the controller's current orientation in space, as a quaternion.
|
||||
/// The rotation is provided in 'orientation space' which means the rotation is given relative
|
||||
/// to the last time the user recentered their controller. To make a game object in your scene
|
||||
/// have the same orientation as the controller, simply assign this quaternion to the object's
|
||||
/// `transform.rotation`. To match the relative rotation, use `transform.localRotation` instead.
|
||||
public Quaternion Orientation {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.orientation;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 Position {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.position;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the controller's current angular speed in radians per second, using the right-hand
|
||||
/// rule (positive means a right-hand rotation about the given axis), as measured by the
|
||||
/// controller's gyroscope.
|
||||
/// The controller's axes are:
|
||||
/// - X points to the right,
|
||||
/// - Y points perpendicularly up from the controller's top surface
|
||||
/// - Z lies along the controller's body, pointing towards the front
|
||||
public Vector3 Gyro {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.gyro;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the controller's current acceleration in meters per second squared.
|
||||
/// The controller's axes are:
|
||||
/// - X points to the right,
|
||||
/// - Y points perpendicularly up from the controller's top surface
|
||||
/// - Z lies along the controller's body, pointing towards the front
|
||||
/// Note that gravity is indistinguishable from acceleration, so when the controller is resting
|
||||
/// on a surface, expect to measure an acceleration of 9.8 m/s^2 on the Y axis. The accelerometer
|
||||
/// reading will be zero on all three axes only if the controller is in free fall, or if the user
|
||||
/// is in a zero gravity environment like a space station.
|
||||
public Vector3 Accel {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.accel;
|
||||
}
|
||||
}
|
||||
|
||||
/// Position of the current touch, if touching the touchpad.
|
||||
/// If not touching, this is the position of the last touch (when the finger left the touchpad).
|
||||
/// The X and Y range is from -1.0 to 1.0. (0, 0) is the center of the touchpad.
|
||||
/// (-.707, -.707) is bottom left, (.707, .707) is upper right.
|
||||
/// The magnitude of the touch vector is guaranteed to be <= 1.0.
|
||||
public Vector2 TouchPos {
|
||||
get {
|
||||
Update();
|
||||
return touchPosCentered;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the user just completed the recenter gesture. The headset and
|
||||
/// the controller's orientation are now being reported in the new recentered
|
||||
/// coordinate system. This is an event flag (it is true for only one frame
|
||||
/// after the event happens, then reverts to false).
|
||||
public bool Recentered {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.recentered;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the user is holding down any of the buttons specified in `buttons`.
|
||||
/// GvrControllerButton types can be OR-ed together to check for multiple buttons at once.
|
||||
public bool GetButton(GvrControllerButton buttons) {
|
||||
Update();
|
||||
return (controllerState.buttonsState & buttons) != 0;
|
||||
}
|
||||
|
||||
/// Returns true in the frame the user starts pressing down any of the buttons specified
|
||||
/// in `buttons`. For an individual button enum, every ButtonDown event is guaranteed to be
|
||||
/// followed by exactly one ButtonUp event in a later frame. Also, ButtonDown and ButtonUp
|
||||
/// will never both be true in the same frame for an individual button. Using multiple button
|
||||
/// enums OR'ed together can result in multiple ButtonDowns before a ButtonUp.
|
||||
public bool GetButtonDown(GvrControllerButton buttons) {
|
||||
Update();
|
||||
return (controllerState.buttonsDown & buttons) != 0;
|
||||
}
|
||||
|
||||
/// Returns true the frame after the user stops pressing down any of the buttons specified
|
||||
/// in `buttons`. For an individual button enum, every ButtonUp event is guaranteed to be
|
||||
/// preceded by exactly one ButtonDown event in an earlier frame. Also, ButtonDown and
|
||||
/// ButtonUp will never both be true in the same frame for an individual button. Using
|
||||
/// multiple button enums OR'ed together can result in multiple ButtonUps after multiple
|
||||
/// ButtonDowns.
|
||||
public bool GetButtonUp(GvrControllerButton buttons) {
|
||||
Update();
|
||||
return (controllerState.buttonsUp & buttons) != 0;
|
||||
}
|
||||
|
||||
/// If State == GvrConnectionState.Error, this contains details about the error.
|
||||
public string ErrorDetails {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.connectionState == GvrConnectionState.Error ?
|
||||
controllerState.errorDetails : "";
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the GVR C library controller state pointer (gvr_controller_state*).
|
||||
public IntPtr StatePtr {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.gvrPtr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the controller is currently being charged.
|
||||
public bool IsCharging {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.isCharging;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the controller's current battery charge level.
|
||||
public GvrControllerBatteryLevel BatteryLevel {
|
||||
get {
|
||||
Update();
|
||||
return controllerState.batteryLevel;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update() {
|
||||
if (lastUpdatedFrameCount != Time.frameCount) {
|
||||
if (!valid) {
|
||||
Debug.LogError("Using an invalid GvrControllerInputDevice. Please acquire a new one from GvrControllerInput.GetDevice().");
|
||||
return;
|
||||
}
|
||||
// The controller state must be updated prior to any function using the
|
||||
// controller API to ensure the state is consistent throughout a frame.
|
||||
lastUpdatedFrameCount = Time.frameCount;
|
||||
|
||||
GvrConnectionState oldState = State;
|
||||
|
||||
controllerProvider.ReadState(controllerState, controllerId);
|
||||
UpdateTouchPosCentered();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (IsDominantHand) {
|
||||
// Make sure the EditorEmulator is updated immediately.
|
||||
if (GvrEditorEmulator.Instance != null) {
|
||||
GvrEditorEmulator.Instance.UpdateEditorEmulation();
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
if (OnStateChanged != null && State != oldState) {
|
||||
OnStateChanged(State, oldState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTouchPosCentered() {
|
||||
touchPosCentered.x = (controllerState.touchPos.x - 0.5f) * 2.0f;
|
||||
touchPosCentered.y = -(controllerState.touchPos.y - 0.5f) * 2.0f;
|
||||
|
||||
float magnitude = touchPosCentered.magnitude;
|
||||
if (magnitude > 1) {
|
||||
touchPosCentered.x /= magnitude;
|
||||
touchPosCentered.y /= magnitude;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b912427c85634a3fa13020a8d93c38a
|
||||
timeCreated: 1519780588
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
163
Assets/GoogleVR/Scripts/Controller/GvrControllerReticleVisual.cs
Normal file
163
Assets/GoogleVR/Scripts/Controller/GvrControllerReticleVisual.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
/// Visualizes a reticle using a Quad.
|
||||
/// Provides tuning options to control how the reticle scales and rotates based
|
||||
/// on distance from the camera.
|
||||
[RequireComponent(typeof(MeshRenderer))]
|
||||
[RequireComponent(typeof(MeshFilter))]
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerReticleVisual")]
|
||||
public class GvrControllerReticleVisual : MonoBehaviour {
|
||||
[Serializable]
|
||||
public struct FaceCameraData {
|
||||
public bool alongXAxis;
|
||||
public bool alongYAxis;
|
||||
public bool alongZAxis;
|
||||
|
||||
public bool IsAnyAxisOff {
|
||||
get {
|
||||
return !alongXAxis || !alongYAxis || !alongZAxis;
|
||||
}
|
||||
}
|
||||
|
||||
public FaceCameraData(bool startEnabled) {
|
||||
alongXAxis = startEnabled;
|
||||
alongYAxis = startEnabled;
|
||||
alongZAxis = startEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/// If set to false, the scale is simply set to the sizeMeters value.
|
||||
[Tooltip("Determines if the size of the reticle is based on the distance from the camera.")]
|
||||
public bool isSizeBasedOnCameraDistance = true;
|
||||
|
||||
/// The reticle will be scaled based on the size of the mesh so that it's size matches this size.
|
||||
[Tooltip("Final size of the reticle in meters when it is 1 meter from the camera.")]
|
||||
public float sizeMeters = 0.1f;
|
||||
|
||||
[Tooltip("Determines if the reticle will always face the camera and along what axes.")]
|
||||
public FaceCameraData doesReticleFaceCamera = new FaceCameraData(true);
|
||||
|
||||
/// Sorting order to use for the reticle's renderer.
|
||||
/// Range values come from https://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html.
|
||||
[Range(-32767, 32767)]
|
||||
public int sortingOrder = 0;
|
||||
|
||||
/// The size of the reticle's mesh in meters.
|
||||
public float ReticleMeshSizeMeters { get; private set; }
|
||||
|
||||
/// The ratio of the reticleMeshSizeMeters to 1 meter.
|
||||
/// If reticleMeshSizeMeters is 10, then reticleMeshSizeRatio is 0.1.
|
||||
public float ReticleMeshSizeRatio { get; private set; }
|
||||
|
||||
protected MeshRenderer meshRenderer;
|
||||
protected MeshFilter meshFilter;
|
||||
|
||||
private Vector3 preRenderLocalScale;
|
||||
private Quaternion preRenderLocalRotation;
|
||||
|
||||
public void RefreshMesh() {
|
||||
ReticleMeshSizeMeters = 1.0f;
|
||||
ReticleMeshSizeRatio = 1.0f;
|
||||
|
||||
if (meshFilter != null && meshFilter.mesh != null) {
|
||||
ReticleMeshSizeMeters = meshFilter.mesh.bounds.size.x;
|
||||
if (ReticleMeshSizeMeters != 0.0f) {
|
||||
ReticleMeshSizeRatio = 1.0f / ReticleMeshSizeMeters;
|
||||
}
|
||||
}
|
||||
|
||||
if (meshRenderer != null) {
|
||||
meshRenderer.sortingOrder = sortingOrder;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Awake() {
|
||||
meshRenderer = GetComponent<MeshRenderer>();
|
||||
meshFilter = GetComponent<MeshFilter>();
|
||||
}
|
||||
|
||||
protected virtual void OnEnable() {
|
||||
RefreshMesh();
|
||||
}
|
||||
|
||||
protected virtual void OnWillRenderObject() {
|
||||
preRenderLocalScale = transform.localScale;
|
||||
preRenderLocalRotation = transform.localRotation;
|
||||
|
||||
Camera camera = Camera.current;
|
||||
UpdateReticleSize(camera);
|
||||
UpdateReticleOrientation(camera);
|
||||
}
|
||||
|
||||
protected virtual void OnRenderObject() {
|
||||
// It is possible for paired calls to OnWillRenderObject/OnRenderObject to be nested if
|
||||
// Camera.Render is explicitly called for any special effects. To avoid the reticle being
|
||||
// rotated/scaled incorrectly in that case, the reticle is reset to it's pre-OnWillRenderObject
|
||||
// after a render has finished.
|
||||
transform.localScale = preRenderLocalScale;
|
||||
transform.localRotation = preRenderLocalRotation;
|
||||
}
|
||||
|
||||
protected virtual void UpdateReticleSize(Camera camera) {
|
||||
if (camera == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float scale = sizeMeters;
|
||||
|
||||
if (isSizeBasedOnCameraDistance) {
|
||||
float reticleDistanceFromCamera = (transform.position - camera.transform.position).magnitude;
|
||||
scale *= ReticleMeshSizeRatio * reticleDistanceFromCamera;
|
||||
}
|
||||
|
||||
transform.localScale = new Vector3(scale, scale, scale);
|
||||
}
|
||||
|
||||
protected virtual void UpdateReticleOrientation(Camera camera) {
|
||||
if (camera == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 direction = transform.position - camera.transform.position;
|
||||
transform.rotation = Quaternion.LookRotation(direction, Vector3.up);
|
||||
|
||||
if (doesReticleFaceCamera.IsAnyAxisOff) {
|
||||
Vector3 euler = transform.localEulerAngles;
|
||||
if (!doesReticleFaceCamera.alongXAxis) {
|
||||
euler.x = 0.0f;
|
||||
}
|
||||
|
||||
if (!doesReticleFaceCamera.alongYAxis) {
|
||||
euler.y = 0.0f;
|
||||
}
|
||||
|
||||
if (!doesReticleFaceCamera.alongZAxis) {
|
||||
euler.z = 0.0f;
|
||||
}
|
||||
|
||||
transform.localEulerAngles = euler;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnValidate() {
|
||||
if (Application.isPlaying && isActiveAndEnabled) {
|
||||
RefreshMesh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5cd7f7fcfc8a4c1fbc201ccc579556d
|
||||
timeCreated: 1495649159
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
347
Assets/GoogleVR/Scripts/Controller/GvrControllerVisual.cs
Normal file
347
Assets/GoogleVR/Scripts/Controller/GvrControllerVisual.cs
Normal file
@@ -0,0 +1,347 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The controller is not available for versions of Unity without the
|
||||
// GVR native integration.
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
/// Provides visual feedback for the daydream controller.
|
||||
[RequireComponent(typeof(Renderer))]
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerVisual")]
|
||||
public class GvrControllerVisual : MonoBehaviour, IGvrArmModelReceiver, IGvrControllerInputDeviceReceiver {
|
||||
[System.Serializable]
|
||||
public struct ControllerDisplayState {
|
||||
|
||||
public GvrControllerBatteryLevel batteryLevel;
|
||||
public bool batteryCharging;
|
||||
|
||||
public bool clickButton;
|
||||
public bool appButton;
|
||||
public bool homeButton;
|
||||
public bool touching;
|
||||
public Vector2 touchPos;
|
||||
}
|
||||
|
||||
/// An array of prefabs that will be instantiated and added as children
|
||||
/// of the controller visual when the controller is created. Used to
|
||||
/// attach tooltips or other additional visual elements to the control dynamically.
|
||||
[SerializeField]
|
||||
private GameObject[] attachmentPrefabs;
|
||||
[SerializeField] private Color touchPadColor =
|
||||
new Color(200f / 255f, 200f / 255f, 200f / 255f, 1);
|
||||
[SerializeField] private Color appButtonColor =
|
||||
new Color(200f / 255f, 200f / 255f, 200f / 255f, 1);
|
||||
[SerializeField] private Color systemButtonColor =
|
||||
new Color(20f / 255f, 20f / 255f, 20f / 255f, 1);
|
||||
|
||||
/// Determines if the displayState is set from GvrControllerInputDevice.
|
||||
[Tooltip("Determines if the displayState is set from GvrControllerInputDevice.")]
|
||||
public bool readControllerState = true;
|
||||
|
||||
/// Used to set the display state of the controller visual.
|
||||
/// This can be used for tutorials that visualize the controller or other use-cases that require
|
||||
/// displaying the controller visual without the state being determined by controller input.
|
||||
/// Additionally, it can be used to preview the controller visual in the editor.
|
||||
/// NOTE: readControllerState must be disabled to set the display state.
|
||||
public ControllerDisplayState displayState;
|
||||
|
||||
/// This is the preferred, maximum alpha value the object should have
|
||||
/// when it is a comfortable distance from the head.
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float maximumAlpha = 1.0f;
|
||||
|
||||
public GvrBaseArmModel ArmModel { get; set; }
|
||||
|
||||
public GvrControllerInputDevice ControllerInputDevice { get; set; }
|
||||
|
||||
public float PreferredAlpha{
|
||||
get{
|
||||
return ArmModel != null ? maximumAlpha * ArmModel.PreferredAlpha : maximumAlpha;
|
||||
}
|
||||
}
|
||||
|
||||
public Color TouchPadColor {
|
||||
get {
|
||||
return touchPadColor;
|
||||
}
|
||||
set {
|
||||
touchPadColor = value;
|
||||
if(materialPropertyBlock != null) {
|
||||
materialPropertyBlock.SetColor(touchPadId, touchPadColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color AppButtonColor {
|
||||
get {
|
||||
return appButtonColor;
|
||||
}
|
||||
set {
|
||||
appButtonColor = value;
|
||||
if(materialPropertyBlock != null){
|
||||
materialPropertyBlock.SetColor(appButtonId, appButtonColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color SystemButtonColor {
|
||||
get {
|
||||
return systemButtonColor;
|
||||
}
|
||||
set {
|
||||
systemButtonColor = value;
|
||||
if(materialPropertyBlock != null) {
|
||||
materialPropertyBlock.SetColor(systemButtonId, systemButtonColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Renderer controllerRenderer;
|
||||
private MaterialPropertyBlock materialPropertyBlock;
|
||||
|
||||
private int alphaId;
|
||||
private int touchId;
|
||||
private int touchPadId;
|
||||
private int appButtonId;
|
||||
private int systemButtonId;
|
||||
private int batteryColorId;
|
||||
|
||||
private bool wasTouching;
|
||||
private float touchTime;
|
||||
|
||||
// Data passed to shader, (xy) touch position, (z) touch duration, (w) battery state.
|
||||
private Vector4 controllerShaderData;
|
||||
// Data passed to shader, (x) overall alpha, (y) touchpad click duration,
|
||||
// (z) app button click duration, (w) system button click duration.
|
||||
private Vector4 controllerShaderData2;
|
||||
private Color currentBatteryColor;
|
||||
|
||||
// These values control animation times for the controller buttons
|
||||
public const float APP_BUTTON_ACTIVE_DURATION_SECONDS = 0.111f;
|
||||
public const float APP_BUTTON_RELEASE_DURATION_SECONDS = 0.0909f;
|
||||
|
||||
public const float SYSTEM_BUTTON_ACTIVE_DURATION_SECONDS = 0.111f;
|
||||
public const float SYSTEM_BUTTON_RELEASE_DURATION_SECONDS = 0.0909f;
|
||||
|
||||
public const float TOUCHPAD_CLICK_DURATION_SECONDS = 0.111f;
|
||||
public const float TOUCHPAD_RELEASE_DURATION_SECONDS = 0.0909f;
|
||||
|
||||
public const float TOUCHPAD_CLICK_SCALE_DURATION_SECONDS = 0.075f;
|
||||
public const float TOUCHPAD_POINT_SCALE_DURATION_SECONDS = 0.15f;
|
||||
|
||||
// These values are used by the shader to control battery display
|
||||
// Only modify these values if you are also modifying the shader.
|
||||
private const float BATTERY_FULL = 0;
|
||||
private const float BATTERY_ALMOST_FULL = .125f;
|
||||
private const float BATTERY_MEDIUM = .225f;
|
||||
private const float BATTERY_LOW = .325f;
|
||||
private const float BATTERY_CRITICAL = .425f;
|
||||
private const float BATTERY_HIDDEN = .525f;
|
||||
|
||||
private readonly Color GVR_BATTERY_CRITICAL_COLOR = new Color(1,0,0,1);
|
||||
private readonly Color GVR_BATTERY_LOW_COLOR = new Color(1,0.6823f,0,1);
|
||||
private readonly Color GVR_BATTERY_MED_COLOR = new Color(0,1,0.588f,1);
|
||||
private readonly Color GVR_BATTERY_FULL_COLOR = new Color(0,1,0.588f,1);
|
||||
|
||||
// How much time to use as an 'immediate update'.
|
||||
// Any value large enough to instantly update all visual animations.
|
||||
private const float IMMEDIATE_UPDATE_TIME = 10f;
|
||||
|
||||
void Awake() {
|
||||
Initialize();
|
||||
CreateAttachments();
|
||||
}
|
||||
|
||||
void OnEnable() {
|
||||
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
|
||||
}
|
||||
|
||||
void OnDisable() {
|
||||
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
|
||||
}
|
||||
|
||||
void OnValidate() {
|
||||
if (!Application.isPlaying) {
|
||||
Initialize();
|
||||
OnVisualUpdate(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPostControllerInputUpdated() {
|
||||
OnVisualUpdate();
|
||||
}
|
||||
|
||||
private void CreateAttachments() {
|
||||
if (attachmentPrefabs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < attachmentPrefabs.Length; i++) {
|
||||
GameObject prefab = attachmentPrefabs[i];
|
||||
GameObject attachment = Instantiate(prefab);
|
||||
attachment.transform.SetParent(transform, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize() {
|
||||
if(controllerRenderer == null) {
|
||||
controllerRenderer = GetComponent<Renderer>();
|
||||
}
|
||||
if(materialPropertyBlock == null) {
|
||||
materialPropertyBlock = new MaterialPropertyBlock();
|
||||
}
|
||||
|
||||
alphaId = Shader.PropertyToID("_GvrControllerAlpha");
|
||||
touchId = Shader.PropertyToID("_GvrTouchInfo");
|
||||
touchPadId = Shader.PropertyToID("_GvrTouchPadColor");
|
||||
appButtonId = Shader.PropertyToID("_GvrAppButtonColor");
|
||||
systemButtonId = Shader.PropertyToID("_GvrSystemButtonColor");
|
||||
batteryColorId = Shader.PropertyToID("_GvrBatteryColor");
|
||||
|
||||
materialPropertyBlock.SetColor(appButtonId, appButtonColor);
|
||||
materialPropertyBlock.SetColor(systemButtonId, systemButtonColor);
|
||||
materialPropertyBlock.SetColor(touchPadId, touchPadColor);
|
||||
controllerRenderer.SetPropertyBlock(materialPropertyBlock);
|
||||
}
|
||||
|
||||
private void UpdateControllerState() {
|
||||
// Return early when the application isn't playing to ensure that the serialized displayState
|
||||
// is used to preview the controller visual instead of the default GvrControllerInputDevice
|
||||
// values.
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(ControllerInputDevice != null) {
|
||||
displayState.batteryLevel = ControllerInputDevice.BatteryLevel;
|
||||
displayState.batteryCharging = ControllerInputDevice.IsCharging;
|
||||
|
||||
displayState.clickButton = ControllerInputDevice.GetButton(GvrControllerButton.TouchPadButton);
|
||||
displayState.appButton = ControllerInputDevice.GetButton(GvrControllerButton.App);
|
||||
displayState.homeButton = ControllerInputDevice.GetButton(GvrControllerButton.System);
|
||||
displayState.touching = ControllerInputDevice.GetButton(GvrControllerButton.TouchPadTouch);
|
||||
displayState.touchPos = ControllerInputDevice.TouchPos;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnVisualUpdate(bool updateImmediately = false) {
|
||||
|
||||
// Update the visual display based on the controller state
|
||||
if(readControllerState) {
|
||||
UpdateControllerState();
|
||||
}
|
||||
|
||||
float deltaTime = Time.deltaTime;
|
||||
|
||||
// If flagged to update immediately, set deltaTime to an arbitrarily large value
|
||||
// This is particularly useful in editor, but also for resetting state quickly
|
||||
if(updateImmediately) {
|
||||
deltaTime = IMMEDIATE_UPDATE_TIME;
|
||||
}
|
||||
|
||||
if (displayState.clickButton) {
|
||||
controllerShaderData2.y = Mathf.Min(1, controllerShaderData2.y + deltaTime / TOUCHPAD_CLICK_DURATION_SECONDS);
|
||||
} else{
|
||||
controllerShaderData2.y = Mathf.Max(0, controllerShaderData2.y - deltaTime / TOUCHPAD_RELEASE_DURATION_SECONDS);
|
||||
}
|
||||
|
||||
if (displayState.appButton) {
|
||||
controllerShaderData2.z = Mathf.Min(1, controllerShaderData2.z + deltaTime / APP_BUTTON_ACTIVE_DURATION_SECONDS);
|
||||
} else{
|
||||
controllerShaderData2.z = Mathf.Max(0, controllerShaderData2.z - deltaTime / APP_BUTTON_RELEASE_DURATION_SECONDS);
|
||||
}
|
||||
|
||||
if (displayState.homeButton) {
|
||||
controllerShaderData2.w = Mathf.Min(1, controllerShaderData2.w + deltaTime / SYSTEM_BUTTON_ACTIVE_DURATION_SECONDS);
|
||||
} else {
|
||||
controllerShaderData2.w = Mathf.Max(0, controllerShaderData2.w - deltaTime / SYSTEM_BUTTON_RELEASE_DURATION_SECONDS);
|
||||
}
|
||||
|
||||
// Set the material's alpha to the multiplied preferred alpha.
|
||||
controllerShaderData2.x = PreferredAlpha;
|
||||
materialPropertyBlock.SetVector(alphaId, controllerShaderData2);
|
||||
|
||||
controllerShaderData.x = displayState.touchPos.x;
|
||||
controllerShaderData.y = displayState.touchPos.y;
|
||||
|
||||
if (displayState.touching || displayState.clickButton) {
|
||||
if (!wasTouching) {
|
||||
wasTouching = true;
|
||||
}
|
||||
if(touchTime < 1) {
|
||||
touchTime = Mathf.Min(touchTime + deltaTime / TOUCHPAD_POINT_SCALE_DURATION_SECONDS, 1);
|
||||
}
|
||||
} else {
|
||||
wasTouching = false;
|
||||
if(touchTime > 0) {
|
||||
touchTime = Mathf.Max(touchTime - deltaTime / TOUCHPAD_POINT_SCALE_DURATION_SECONDS, 0);
|
||||
}
|
||||
}
|
||||
|
||||
controllerShaderData.z = touchTime;
|
||||
|
||||
UpdateBatteryIndicator();
|
||||
|
||||
materialPropertyBlock.SetVector(touchId, controllerShaderData);
|
||||
materialPropertyBlock.SetColor(batteryColorId, currentBatteryColor);
|
||||
// Update the renderer
|
||||
controllerRenderer.SetPropertyBlock(materialPropertyBlock);
|
||||
}
|
||||
|
||||
private void UpdateBatteryIndicator() {
|
||||
|
||||
GvrControllerBatteryLevel level = displayState.batteryLevel;
|
||||
bool charging = displayState.batteryCharging;
|
||||
|
||||
switch (level) {
|
||||
case GvrControllerBatteryLevel.Full:
|
||||
controllerShaderData.w = BATTERY_FULL;
|
||||
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
|
||||
break;
|
||||
case GvrControllerBatteryLevel.AlmostFull:
|
||||
controllerShaderData.w = BATTERY_ALMOST_FULL;
|
||||
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
|
||||
break;
|
||||
case GvrControllerBatteryLevel.Medium:
|
||||
controllerShaderData.w = BATTERY_MEDIUM;
|
||||
currentBatteryColor = GVR_BATTERY_MED_COLOR;
|
||||
break;
|
||||
case GvrControllerBatteryLevel.Low:
|
||||
controllerShaderData.w = BATTERY_LOW;
|
||||
currentBatteryColor = GVR_BATTERY_LOW_COLOR;
|
||||
break;
|
||||
case GvrControllerBatteryLevel.CriticalLow:
|
||||
controllerShaderData.w = BATTERY_CRITICAL;
|
||||
currentBatteryColor = GVR_BATTERY_CRITICAL_COLOR;
|
||||
break;
|
||||
default:
|
||||
controllerShaderData.w = BATTERY_HIDDEN;
|
||||
currentBatteryColor.a = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (charging) {
|
||||
controllerShaderData.w = -controllerShaderData.w;
|
||||
currentBatteryColor = GVR_BATTERY_FULL_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetControllerTexture(Texture newTexture) {
|
||||
controllerRenderer.material.mainTexture = newTexture;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa952cbcc0eb13d4ca558b6da550ff55
|
||||
timeCreated: 1472074640
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
123
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs
Normal file
123
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The controller is not available for versions of Unity without the
|
||||
// GVR native integration.
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
/// Implementation of GvrBasePointer for a laser pointer visual.
|
||||
/// This script should be attached to the controller object.
|
||||
/// The laser visual is important to help users locate their cursor
|
||||
/// when its not directly in their field of view.
|
||||
[RequireComponent(typeof(GvrLaserVisual))]
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrLaserPointer")]
|
||||
public class GvrLaserPointer : GvrBasePointer {
|
||||
[Tooltip("Distance from the pointer that raycast hits will be detected.")]
|
||||
public float maxPointerDistance = 20.0f;
|
||||
|
||||
[Tooltip("Distance from the pointer that the reticle will be drawn at when hitting nothing.")]
|
||||
public float defaultReticleDistance = 20.0f;
|
||||
|
||||
[Tooltip("By default, the length of the laser is used as the CameraRayIntersectionDistance. " +
|
||||
"Set this field to a non-zero value to override it.")]
|
||||
public float overrideCameraRayIntersectionDistance;
|
||||
|
||||
/// The percentage of the reticle mesh that shows the reticle.
|
||||
/// The rest of the reticle mesh is transparent.
|
||||
private const float RETICLE_VISUAL_RATIO = 0.1f;
|
||||
|
||||
public GvrLaserVisual LaserVisual { get; private set; }
|
||||
|
||||
private bool isHittingTarget;
|
||||
|
||||
public override float MaxPointerDistance {
|
||||
get {
|
||||
return maxPointerDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public override float CameraRayIntersectionDistance {
|
||||
get {
|
||||
if (overrideCameraRayIntersectionDistance != 0.0f) {
|
||||
return overrideCameraRayIntersectionDistance;
|
||||
}
|
||||
|
||||
return LaserVisual != null ?
|
||||
LaserVisual.maxLaserDistance : overrideCameraRayIntersectionDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnPointerEnter(RaycastResult raycastResult, bool isInteractive) {
|
||||
LaserVisual.SetDistance(raycastResult.distance);
|
||||
isHittingTarget = true;
|
||||
}
|
||||
|
||||
public override void OnPointerHover(RaycastResult raycastResult, bool isInteractive) {
|
||||
LaserVisual.SetDistance(raycastResult.distance);
|
||||
isHittingTarget = true;
|
||||
}
|
||||
|
||||
public override void OnPointerExit(GameObject previousObject) {
|
||||
// Don't set the distance immediately.
|
||||
// If we exit/enter an object on the same frame, then SetDistance
|
||||
// will be called twice which could cause an issue with lerping the reticle.
|
||||
// If we don't re-enter a new object, the distance will be set in Update.
|
||||
isHittingTarget = false;
|
||||
}
|
||||
|
||||
public override void OnPointerClickDown() {
|
||||
}
|
||||
|
||||
public override void OnPointerClickUp() {
|
||||
}
|
||||
|
||||
public override void GetPointerRadius(out float enterRadius, out float exitRadius) {
|
||||
if (LaserVisual.reticle != null) {
|
||||
float reticleScale = LaserVisual.reticle.transform.localScale.x;
|
||||
|
||||
// Fixed size for enter radius to avoid flickering.
|
||||
// This will cause some slight variability based on the distance of the object
|
||||
// from the camera, and is optimized for the average case.
|
||||
enterRadius = LaserVisual.reticle.sizeMeters * 0.5f * RETICLE_VISUAL_RATIO;
|
||||
|
||||
// Dynamic size for exit radius.
|
||||
// Always correct because we know the intersection point of the object and can
|
||||
// therefore use the correct radius based on the object's distance from the camera.
|
||||
exitRadius = reticleScale * LaserVisual.reticle.ReticleMeshSizeMeters * RETICLE_VISUAL_RATIO;
|
||||
} else {
|
||||
enterRadius = 0.0f;
|
||||
exitRadius = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake() {
|
||||
LaserVisual = GetComponent<GvrLaserVisual>();
|
||||
}
|
||||
|
||||
protected override void Start() {
|
||||
base.Start();
|
||||
LaserVisual.GetPointForDistanceFunction = GetPointAlongPointer;
|
||||
LaserVisual.SetDistance(defaultReticleDistance, true);
|
||||
}
|
||||
|
||||
void Update() {
|
||||
if (isHittingTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
LaserVisual.SetDistance(defaultReticleDistance);
|
||||
}
|
||||
}
|
||||
12
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs.meta
Normal file
12
Assets/GoogleVR/Scripts/Controller/GvrLaserPointer.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 51b65678ccdd949e9a58874d2880c0ef
|
||||
timeCreated: 1472486489
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
240
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs
Normal file
240
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
/// Visualizes a laser and a reticle using a LineRenderer and a Quad.
|
||||
/// Provides functions for settings the end point of the laser,
|
||||
/// and clamps the laser and reticle based on max distances.
|
||||
[RequireComponent(typeof(LineRenderer))]
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrLaserVisual")]
|
||||
public class GvrLaserVisual : MonoBehaviour, IGvrArmModelReceiver {
|
||||
/// Used to position the reticle at the current position.
|
||||
[Tooltip("Used to position the reticle at the current position.")]
|
||||
public GvrControllerReticleVisual reticle;
|
||||
|
||||
/// The end point of the visual will not necessarily be along the forward direction of the laser.
|
||||
/// This is particularly true in both Camera and Hybrid Raycast Modes. In that case, both the
|
||||
/// laser and the controller are rotated to face the end point. This reference is used to control
|
||||
/// the rotation of the controller.
|
||||
[Tooltip("Used to rotate the controller to face the current position.")]
|
||||
public Transform controller;
|
||||
|
||||
/// Color of the laser pointer including alpha transparency.
|
||||
[Tooltip("Start color of the laser pointer including alpha transparency.")]
|
||||
public Color laserColor = new Color(1.0f, 1.0f, 1.0f, 0.25f);
|
||||
|
||||
/// Color of the laser pointer including alpha transparency.
|
||||
[Tooltip("End color of the laser pointer including alpha transparency.")]
|
||||
public Color laserColorEnd = new Color(1.0f, 1.0f, 1.0f, 0.0f);
|
||||
|
||||
/// Maximum distance of the laser (meters).
|
||||
[Tooltip("Maximum distance of the laser (meters).")]
|
||||
[Range(0.0f, 20.0f)]
|
||||
public float maxLaserDistance = 1.0f;
|
||||
|
||||
/// The rate that the current position moves towards the target position.
|
||||
[Tooltip("The rate that the current position moves towards the target position.")]
|
||||
public float lerpSpeed = 20.0f;
|
||||
|
||||
/// If the targetPosition is greater than this threshold, then
|
||||
/// the position changes immediately instead of lerping.
|
||||
[Tooltip("If the target position is greater than this threshold, then the position changes " +
|
||||
"immediately instead of lerping.")]
|
||||
public float lerpThreshold = 1.0f;
|
||||
|
||||
/// This is primarily used for Hybrid Raycast mode (details in _GvrBasePointer_) to prevent
|
||||
/// mismatches between the laser and the reticle when the "camera" component of the ray is used.
|
||||
[Tooltip("Determines if the laser will shrink when it isn't facing in the forward direction " +
|
||||
"of the transform.")]
|
||||
public bool shrinkLaser = true;
|
||||
|
||||
/// Amount to shrink the laser when it is fully shrunk.
|
||||
[Range(0.0f, 1.0f)]
|
||||
[Tooltip("Amount to shrink the laser when it is fully shrunk.")]
|
||||
public float shrunkScale = 0.2f;
|
||||
|
||||
/// Begin shrinking the laser when the angle between transform.forward and the reticle
|
||||
/// is greater than this value.
|
||||
[Range(0.0f, 15.0f)]
|
||||
[Tooltip("Begin shrinking the laser when the angle between transform.forward and the reticle " +
|
||||
"is greater than this value.")]
|
||||
public float beginShrinkAngleDegrees = 0.0f;
|
||||
|
||||
/// Finish shrinking the laser when the angle between transform.forward and the reticle is
|
||||
/// greater than this value.
|
||||
[Range(0.0f, 15.0f)]
|
||||
[Tooltip("Finish shrinking the laser when the angle between transform.forward and the reticle " +
|
||||
"is greater than this value.")]
|
||||
public float endShrinkAngleDegrees = 2.0f;
|
||||
|
||||
private const float LERP_CLAMP_THRESHOLD = 0.02f;
|
||||
|
||||
public GvrBaseArmModel ArmModel { get; set; }
|
||||
|
||||
/// Reference to the laser's line renderer.
|
||||
public LineRenderer Laser { get; private set; }
|
||||
|
||||
/// Optional delegate for customizing how the currentPosition is calculated based on the distance.
|
||||
/// If not set, the currentPosition is determined based on the distance multiplied by the forward
|
||||
/// direction of the transform added to the position of the transform.
|
||||
public delegate Vector3 GetPointForDistanceDelegate(float distance);
|
||||
|
||||
public GetPointForDistanceDelegate GetPointForDistanceFunction { get; set; }
|
||||
|
||||
protected float shrinkRatio;
|
||||
protected float targetDistance;
|
||||
protected float currentDistance;
|
||||
protected Vector3 currentPosition;
|
||||
protected Vector3 currentLocalPosition;
|
||||
protected Quaternion currentLocalRotation;
|
||||
|
||||
/// Set the distance of the laser.
|
||||
/// Clamps the distance of the laser and reticle.
|
||||
///
|
||||
/// **distance** target distance from the pointer to draw the visual at.
|
||||
/// **immediate** If true, the distance is changed immediately. Otherwise, it will lerp.
|
||||
public virtual void SetDistance(float distance, bool immediate = false) {
|
||||
targetDistance = distance;
|
||||
if (immediate) {
|
||||
currentDistance = targetDistance;
|
||||
}
|
||||
|
||||
if (targetDistance > lerpThreshold) {
|
||||
currentDistance = targetDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public float CurrentDistance {
|
||||
get { return currentDistance; }
|
||||
}
|
||||
|
||||
protected virtual void Awake() {
|
||||
Laser = GetComponent<LineRenderer>();
|
||||
}
|
||||
|
||||
protected virtual void LateUpdate() {
|
||||
UpdateCurrentPosition();
|
||||
UpdateControllerOrientation();
|
||||
UpdateReticlePosition();
|
||||
UpdateLaserEndPoint();
|
||||
UpdateLaserAlpha();
|
||||
}
|
||||
|
||||
protected virtual void UpdateCurrentPosition() {
|
||||
if (currentDistance != targetDistance) {
|
||||
float speed = GetSpeed();
|
||||
currentDistance = Mathf.Lerp(currentDistance, targetDistance, speed);
|
||||
float diff = Mathf.Abs(targetDistance - currentDistance);
|
||||
if (diff < LERP_CLAMP_THRESHOLD) {
|
||||
currentDistance = targetDistance;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetPointForDistanceFunction != null) {
|
||||
currentPosition = GetPointForDistanceFunction(currentDistance);
|
||||
} else {
|
||||
Vector3 origin = transform.position;
|
||||
currentPosition = origin + (transform.forward * currentDistance);
|
||||
}
|
||||
|
||||
currentLocalPosition = transform.InverseTransformPoint(currentPosition);
|
||||
currentLocalRotation = Quaternion.FromToRotation(Vector3.forward, currentLocalPosition);
|
||||
}
|
||||
|
||||
protected virtual void UpdateControllerOrientation() {
|
||||
if (controller == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
controller.localRotation = currentLocalRotation;
|
||||
}
|
||||
|
||||
protected virtual void UpdateReticlePosition() {
|
||||
if (reticle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
reticle.transform.position = currentPosition;
|
||||
}
|
||||
|
||||
protected virtual void UpdateLaserEndPoint() {
|
||||
if (Laser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 laserStartPoint = Vector3.zero;
|
||||
Vector3 laserEndPoint;
|
||||
|
||||
if (controller != null) {
|
||||
Vector3 worldPosition = transform.position;
|
||||
Vector3 rotatedPosition = controller.InverseTransformPoint(worldPosition);
|
||||
rotatedPosition = currentLocalRotation * rotatedPosition;
|
||||
laserStartPoint = controller.TransformPoint(rotatedPosition);
|
||||
laserStartPoint = transform.InverseTransformPoint(laserStartPoint);
|
||||
}
|
||||
|
||||
laserEndPoint = Vector3.ClampMagnitude(currentLocalPosition, maxLaserDistance);
|
||||
|
||||
if (shrinkLaser) {
|
||||
// Calculate the angle of rotation in degrees.
|
||||
float angle = Vector3.Angle(Vector3.forward, currentLocalPosition);
|
||||
|
||||
// Calculate the shrink ratio based on the angle.
|
||||
float shrinkAngleDelta = endShrinkAngleDegrees - beginShrinkAngleDegrees;
|
||||
float clampedAngle = Mathf.Clamp(angle - beginShrinkAngleDegrees, 0.0f, shrinkAngleDelta);
|
||||
shrinkRatio = clampedAngle / shrinkAngleDelta;
|
||||
|
||||
// Calculate the shrink coeff.
|
||||
float shrinkCoeff = GvrMathHelpers.EaseOutCubic(shrunkScale, 1.0f, 1.0f - shrinkRatio);
|
||||
|
||||
// Calculate the final distance of the laser.
|
||||
Vector3 diff = laserStartPoint - currentLocalPosition;
|
||||
Vector3 dir = diff.normalized;
|
||||
float dist = Mathf.Min(diff.magnitude, maxLaserDistance) * shrinkCoeff;
|
||||
|
||||
// Update the laser start and end points.
|
||||
laserEndPoint = currentLocalPosition;
|
||||
laserStartPoint = laserEndPoint + (dir * dist);
|
||||
}
|
||||
|
||||
Laser.useWorldSpace = false;
|
||||
Laser.SetPosition(0, laserStartPoint);
|
||||
Laser.SetPosition(1, laserEndPoint);
|
||||
}
|
||||
|
||||
protected virtual void UpdateLaserAlpha() {
|
||||
float alpha = ArmModel != null ? ArmModel.PreferredAlpha : 1.0f;
|
||||
|
||||
Color finalStartColor = Color.Lerp(Color.clear, laserColor, alpha);
|
||||
Color finalEndColor = laserColorEnd;
|
||||
|
||||
// If shrinking the laser, the colors are inversed based on the shrink ratio.
|
||||
// This is to ensure that the feathering of the laser goes in the right direction.
|
||||
if (shrinkLaser) {
|
||||
float colorRatio = GvrMathHelpers.EaseOutCubic(0.0f, 1.0f, shrinkRatio);
|
||||
finalEndColor = Color.Lerp(finalEndColor, finalStartColor, colorRatio);
|
||||
finalStartColor = Color.Lerp(finalStartColor, laserColorEnd, colorRatio);
|
||||
}
|
||||
|
||||
Laser.startColor = finalStartColor;
|
||||
Laser.endColor = finalEndColor;
|
||||
}
|
||||
|
||||
protected virtual float GetSpeed() {
|
||||
return lerpSpeed > 0.0f ? lerpSpeed * Time.unscaledDeltaTime : 1.0f;
|
||||
}
|
||||
}
|
||||
12
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs.meta
Normal file
12
Assets/GoogleVR/Scripts/Controller/GvrLaserVisual.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4ee199cdc1304b2090f019e4f488674
|
||||
timeCreated: 1495649159
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
using UnityEngine.XR;
|
||||
#else
|
||||
using XRSettings = UnityEngine.VR.VRSettings;
|
||||
#endif // UNITY_2017_2_OR_NEWER
|
||||
|
||||
/// Used to recenter only the controllers, required for scenes that have no clear forward direction.
|
||||
/// Details: https://developers.google.com/vr/distribute/daydream/design-requirements#UX-D6
|
||||
///
|
||||
/// Works by offsetting the orientation of the transform when a recenter occurs to correct for the
|
||||
/// orientation change caused by the recenter event.
|
||||
///
|
||||
/// Usage: Place on the parent of the camera that should have it's orientation corrected.
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrRecenterOnlyController")]
|
||||
public class GvrRecenterOnlyController : MonoBehaviour {
|
||||
private Quaternion lastAppliedYawCorrection = Quaternion.identity;
|
||||
private Quaternion yawCorrection = Quaternion.identity;
|
||||
|
||||
void Update() {
|
||||
bool connected = false;
|
||||
foreach (var hand in Gvr.Internal.ControllerUtils.AllHands) {
|
||||
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
|
||||
if (device.State == GvrConnectionState.Connected) {
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Daydream is loaded only on deivce, not in editor.
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
if (XRSettings.loadedDeviceName != GvrSettings.VR_SDK_DAYDREAM) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (GvrControllerInput.Recentered) {
|
||||
ApplyYawCorrection();
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// Compatibility for Instant Preview.
|
||||
if (Gvr.Internal.InstantPreview.Instance != null &&
|
||||
Gvr.Internal.InstantPreview.Instance.enabled &&
|
||||
Gvr.Internal.ControllerUtils.AnyButton(GvrControllerButton.System)) {
|
||||
return;
|
||||
}
|
||||
#else // !UNITY_EDITOR
|
||||
if (Gvr.Internal.ControllerUtils.AnyButton(GvrControllerButton.System)) {
|
||||
return;
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
yawCorrection = GetYawCorrection();
|
||||
}
|
||||
|
||||
void OnDisable() {
|
||||
yawCorrection = Quaternion.identity;
|
||||
RemoveLastYawCorrection();
|
||||
}
|
||||
|
||||
private void ApplyYawCorrection() {
|
||||
RemoveLastYawCorrection();
|
||||
transform.localRotation = transform.localRotation * yawCorrection;
|
||||
lastAppliedYawCorrection = yawCorrection;
|
||||
}
|
||||
|
||||
private void RemoveLastYawCorrection() {
|
||||
transform.localRotation =
|
||||
transform.localRotation * Quaternion.Inverse(lastAppliedYawCorrection);
|
||||
lastAppliedYawCorrection = Quaternion.identity;
|
||||
}
|
||||
|
||||
private Quaternion GetYawCorrection() {
|
||||
Quaternion headRotation = GvrVRHelpers.GetHeadRotation();
|
||||
Vector3 euler = headRotation.eulerAngles;
|
||||
return lastAppliedYawCorrection * Quaternion.Euler(0.0f, euler.y, 0.0f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5d8d09b9e5d2437aa022780a2ce8c83
|
||||
timeCreated: 1487015053
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
238
Assets/GoogleVR/Scripts/Controller/GvrTrackedController.cs
Normal file
238
Assets/GoogleVR/Scripts/Controller/GvrTrackedController.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
/// Represents an object tracked by controller input.
|
||||
/// Manages the active status of the tracked controller based on controller connection status.
|
||||
/// Fetches a `GvrControllerInputDevice` for the configured `GvrControllerHand` and propagates
|
||||
/// the device instance to all `IGvrControllerInputDeviceReceiver`s underneath this object on
|
||||
/// Start and if the controller handedness changes. If the controller is not positionally
|
||||
/// tracked, position of the object is updated to approximate arm mechanics by using a
|
||||
/// `GvrBaseArmModel`. `GvrBaseArmModel`s are also propagated to all `IGvrArmModelReceiver`s
|
||||
/// underneath this object.
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrTrackedController")]
|
||||
public class GvrTrackedController : MonoBehaviour {
|
||||
[SerializeField]
|
||||
[Tooltip("Arm model used to control the pose (position and rotation) of the object, " +
|
||||
"and to propagate to children that implement IGvrArmModelReceiver.")]
|
||||
private GvrBaseArmModel armModel;
|
||||
private GvrControllerInputDevice controllerInputDevice;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Is the object's active status determined by the controller connection status.")]
|
||||
private bool isDeactivatedWhenDisconnected = true;
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Controller Hand")]
|
||||
private GvrControllerHand controllerHand = GvrControllerHand.Dominant;
|
||||
|
||||
public GvrControllerInputDevice ControllerInputDevice {
|
||||
get {
|
||||
return controllerInputDevice;
|
||||
}
|
||||
}
|
||||
|
||||
public GvrControllerHand ControllerHand {
|
||||
get {
|
||||
return controllerHand;
|
||||
}
|
||||
set {
|
||||
if (value != controllerHand) {
|
||||
controllerHand = value;
|
||||
SetupControllerInputDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Arm model used to control the pose (position and rotation) of the object, and to propagate to
|
||||
/// children that implement IGvrArmModelReceiver.
|
||||
public GvrBaseArmModel ArmModel {
|
||||
get {
|
||||
return armModel;
|
||||
}
|
||||
set {
|
||||
if (armModel == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
armModel = value;
|
||||
PropagateControllerInputDeviceToArmModel();
|
||||
PropagateArmModel();
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the object's active status determined by the controller connection status.
|
||||
public bool IsDeactivatedWhenDisconnected {
|
||||
get {
|
||||
return isDeactivatedWhenDisconnected;
|
||||
}
|
||||
set {
|
||||
if (isDeactivatedWhenDisconnected == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
isDeactivatedWhenDisconnected = value;
|
||||
|
||||
if (isDeactivatedWhenDisconnected) {
|
||||
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PropagateArmModel() {
|
||||
IGvrArmModelReceiver[] receivers =
|
||||
GetComponentsInChildren<IGvrArmModelReceiver>(true);
|
||||
|
||||
for (int i = 0; i < receivers.Length; i++) {
|
||||
IGvrArmModelReceiver receiver = receivers[i];
|
||||
receiver.ArmModel = armModel;
|
||||
}
|
||||
}
|
||||
|
||||
void Awake() {
|
||||
// Adding this event handler calls it immediately.
|
||||
GvrControllerInput.OnDevicesChanged += SetupControllerInputDevice;
|
||||
}
|
||||
|
||||
void OnEnable() {
|
||||
// Print an error to console if no GvrControllerInput is found.
|
||||
if (controllerInputDevice.State == GvrConnectionState.Error) {
|
||||
Debug.LogWarning(controllerInputDevice.ErrorDetails);
|
||||
}
|
||||
|
||||
// Update the position using OnPostControllerInputUpdated.
|
||||
// This way, the position and rotation will be correct for the entire frame
|
||||
// so that it doesn't matter what order Updates get called in.
|
||||
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
|
||||
|
||||
/// Force the pose to update immediately in case the controller isn't updated before the next
|
||||
/// time a frame is rendered.
|
||||
UpdatePose();
|
||||
|
||||
/// Check the controller state immediately whenever this script is enabled.
|
||||
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
|
||||
}
|
||||
|
||||
void OnDisable() {
|
||||
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
|
||||
}
|
||||
|
||||
void Start() {
|
||||
PropagateArmModel();
|
||||
if (controllerInputDevice != null) {
|
||||
PropagateControllerInputDevice();
|
||||
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
GvrControllerInput.OnDevicesChanged -= SetupControllerInputDevice;
|
||||
if (controllerInputDevice != null) {
|
||||
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
|
||||
controllerInputDevice = null;
|
||||
PropagateControllerInputDevice();
|
||||
}
|
||||
}
|
||||
|
||||
private void PropagateControllerInputDevice() {
|
||||
IGvrControllerInputDeviceReceiver[] receivers =
|
||||
GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
|
||||
|
||||
foreach (var receiver in receivers) {
|
||||
receiver.ControllerInputDevice = controllerInputDevice;
|
||||
}
|
||||
PropagateControllerInputDeviceToArmModel();
|
||||
}
|
||||
|
||||
private void PropagateControllerInputDeviceToArmModel() {
|
||||
// Propagate the controller input device to everything in the arm model's object's
|
||||
// hierarchy in case it is not a child of the tracked controller.
|
||||
if (armModel != null) {
|
||||
IGvrControllerInputDeviceReceiver[] receivers =
|
||||
armModel.GetComponentsInChildren<IGvrControllerInputDeviceReceiver>(true);
|
||||
|
||||
foreach (var receiver in receivers) {
|
||||
receiver.ControllerInputDevice = controllerInputDevice;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupControllerInputDevice() {
|
||||
GvrControllerInputDevice newDevice = GvrControllerInput.GetDevice(controllerHand);
|
||||
if (controllerInputDevice == newDevice) {
|
||||
return;
|
||||
}
|
||||
if (controllerInputDevice != null) {
|
||||
controllerInputDevice.OnStateChanged -= OnControllerStateChanged;
|
||||
controllerInputDevice = null;
|
||||
}
|
||||
|
||||
controllerInputDevice = newDevice;
|
||||
if (controllerInputDevice != null) {
|
||||
controllerInputDevice.OnStateChanged += OnControllerStateChanged;
|
||||
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
|
||||
} else {
|
||||
OnControllerStateChanged(GvrConnectionState.Disconnected, GvrConnectionState.Disconnected);
|
||||
}
|
||||
PropagateControllerInputDevice();
|
||||
}
|
||||
|
||||
private void OnPostControllerInputUpdated() {
|
||||
UpdatePose();
|
||||
}
|
||||
|
||||
private void OnControllerStateChanged(GvrConnectionState state, GvrConnectionState oldState) {
|
||||
if (isDeactivatedWhenDisconnected && enabled) {
|
||||
gameObject.SetActive(state == GvrConnectionState.Connected);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePose() {
|
||||
if (controllerInputDevice == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-positionally tracked controllers always return Position of Vector3.zero.
|
||||
if (controllerInputDevice.Position != Vector3.zero) {
|
||||
transform.localPosition = controllerInputDevice.Position;
|
||||
transform.localRotation = controllerInputDevice.Orientation;
|
||||
} else {
|
||||
if (armModel == null || !controllerInputDevice.IsDominantHand) {
|
||||
return;
|
||||
}
|
||||
|
||||
transform.localPosition = ArmModel.ControllerPositionFromHead;
|
||||
transform.localRotation = ArmModel.ControllerRotationFromHead;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// If the "armModel" serialized field is changed while the application is playing
|
||||
/// by using the inspector in the editor, then we need to call the PropagateArmModel
|
||||
/// to ensure all children IGvrArmModelReceiver are updated.
|
||||
/// Outside of the editor, this can't happen because the arm model can only change when
|
||||
/// a Setter is called that automatically calls PropagateArmModel.
|
||||
void OnValidate() {
|
||||
if (Application.isPlaying && isActiveAndEnabled) {
|
||||
PropagateArmModel();
|
||||
if (controllerInputDevice != null) {
|
||||
OnControllerStateChanged(controllerInputDevice.State, controllerInputDevice.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 311793381eb9d45149dc1a422000a9fd
|
||||
timeCreated: 1481762795
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2018 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public interface IGvrControllerInputDeviceReceiver {
|
||||
GvrControllerInputDevice ControllerInputDevice { set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a4fdc21aee934d82baa564e797da921
|
||||
timeCreated: 1519951901
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GoogleVR/Scripts/Controller/Internal.meta
Normal file
8
Assets/GoogleVR/Scripts/Controller/Internal.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8d1351fa06562347ad7429db4b16b7c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Factory that provides a concrete implementation of IControllerProvider for the
|
||||
/// current platform.
|
||||
static class ControllerProviderFactory {
|
||||
/// Provides a concrete implementation of IControllerProvider appropriate for the current
|
||||
/// platform. This method never returns null. In the worst case, it might return a dummy
|
||||
/// provider if the platform is not supported. For demo purposes the emulator controller
|
||||
/// is returned in the editor and in Standalone buids, for use inside the desktop player.
|
||||
static internal IControllerProvider CreateControllerProvider(GvrControllerInput owner) {
|
||||
// Use emualtor in editor, and in Standalone builds (for demo purposes).
|
||||
#if UNITY_EDITOR
|
||||
// Use the Editor controller provider that supports the controller emulator and the mouse.
|
||||
return new EditorControllerProvider(owner.emulatorConnectionMode);
|
||||
#elif UNITY_ANDROID
|
||||
// Use the GVR C API.
|
||||
return new AndroidNativeControllerProvider();
|
||||
#else
|
||||
// Platform not supported.
|
||||
Debug.LogWarning("No controller support on this platform.");
|
||||
return new DummyControllerProvider();
|
||||
#endif // UNITY_EDITOR || UNITY_ANDROID
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f9210011b71142d5966eec2db6cc696
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8adfc793bee33b24b861e195c2b5b483
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,459 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This provider is only available on an Android device.
|
||||
#if UNITY_ANDROID && !UNITY_EDITOR
|
||||
using UnityEngine;
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Controller Provider that uses the native GVR C API to communicate with controllers
|
||||
/// via Google VR Services on Android.
|
||||
class AndroidNativeControllerProvider : IControllerProvider {
|
||||
// Note: keep structs and function signatures in sync with the C header file (gvr_controller.h).
|
||||
// GVR controller option flags.
|
||||
private const int GVR_CONTROLLER_ENABLE_ORIENTATION = 1 << 0;
|
||||
private const int GVR_CONTROLLER_ENABLE_TOUCH = 1 << 1;
|
||||
private const int GVR_CONTROLLER_ENABLE_GYRO = 1 << 2;
|
||||
private const int GVR_CONTROLLER_ENABLE_ACCEL = 1 << 3;
|
||||
private const int GVR_CONTROLLER_ENABLE_GESTURES = 1 << 4;
|
||||
private const int GVR_CONTROLLER_ENABLE_POSE_PREDICTION = 1 << 5;
|
||||
private const int GVR_CONTROLLER_ENABLE_POSITION = 1 << 6;
|
||||
private const int GVR_CONTROLLER_ENABLE_BATTERY = 1 << 7;
|
||||
private const int GVR_CONTROLLER_ENABLE_ARM_MODEL = 1 << 8;
|
||||
|
||||
// enum gvr_controller_button:
|
||||
private const int GVR_CONTROLLER_BUTTON_NONE = 0;
|
||||
private const int GVR_CONTROLLER_BUTTON_CLICK = 1;
|
||||
private const int GVR_CONTROLLER_BUTTON_HOME = 2;
|
||||
private const int GVR_CONTROLLER_BUTTON_APP = 3;
|
||||
private const int GVR_CONTROLLER_BUTTON_VOLUME_UP = 4;
|
||||
private const int GVR_CONTROLLER_BUTTON_VOLUME_DOWN = 5;
|
||||
private const int GVR_CONTROLLER_BUTTON_RESERVED0 = 6;
|
||||
private const int GVR_CONTROLLER_BUTTON_RESERVED1 = 7;
|
||||
private const int GVR_CONTROLLER_BUTTON_RESERVED2 = 8;
|
||||
private const int GVR_CONTROLLER_BUTTON_COUNT = 9;
|
||||
|
||||
// enum gvr_controller_connection_state:
|
||||
private const int GVR_CONTROLLER_DISCONNECTED = 0;
|
||||
private const int GVR_CONTROLLER_SCANNING = 1;
|
||||
private const int GVR_CONTROLLER_CONNECTING = 2;
|
||||
private const int GVR_CONTROLLER_CONNECTED = 3;
|
||||
|
||||
// enum gvr_controller_api_status
|
||||
private const int GVR_CONTROLLER_API_OK = 0;
|
||||
private const int GVR_CONTROLLER_API_UNSUPPORTED = 1;
|
||||
private const int GVR_CONTROLLER_API_NOT_AUTHORIZED = 2;
|
||||
private const int GVR_CONTROLLER_API_UNAVAILABLE = 3;
|
||||
private const int GVR_CONTROLLER_API_SERVICE_OBSOLETE = 4;
|
||||
private const int GVR_CONTROLLER_API_CLIENT_OBSOLETE = 5;
|
||||
private const int GVR_CONTROLLER_API_MALFUNCTION = 6;
|
||||
|
||||
// The serialization of button-state used to determine which buttons are being pressed.
|
||||
private readonly GvrControllerButton[] GVR_UNITY_BUTTONS = new GvrControllerButton[] {
|
||||
GvrControllerButton.App,
|
||||
GvrControllerButton.System,
|
||||
GvrControllerButton.TouchPadButton,
|
||||
GvrControllerButton.Reserved0,
|
||||
GvrControllerButton.Reserved1,
|
||||
GvrControllerButton.Reserved2
|
||||
};
|
||||
private readonly int[] GVR_BUTTONS = new int[] {
|
||||
GVR_CONTROLLER_BUTTON_APP,
|
||||
GVR_CONTROLLER_BUTTON_HOME,
|
||||
GVR_CONTROLLER_BUTTON_CLICK,
|
||||
GVR_CONTROLLER_BUTTON_RESERVED0,
|
||||
GVR_CONTROLLER_BUTTON_RESERVED1,
|
||||
GVR_CONTROLLER_BUTTON_RESERVED2
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct gvr_quat {
|
||||
internal float x;
|
||||
internal float y;
|
||||
internal float z;
|
||||
internal float w;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct gvr_vec3 {
|
||||
internal float x;
|
||||
internal float y;
|
||||
internal float z;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct gvr_vec2 {
|
||||
internal float x;
|
||||
internal float y;
|
||||
}
|
||||
|
||||
private const string dllName = GvrActivityHelper.GVR_DLL_NAME;
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_get_default_options();
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern IntPtr gvr_controller_create_and_init_android(
|
||||
IntPtr jniEnv, IntPtr androidContext, IntPtr classLoader,
|
||||
int options, IntPtr context);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_destroy(ref IntPtr api);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_pause(IntPtr api);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_resume(IntPtr api);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern IntPtr gvr_controller_state_create();
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_state_destroy(ref IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern void gvr_controller_state_update(IntPtr api, int flags, IntPtr out_state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_state_get_api_status(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_state_get_connection_state(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_quat gvr_controller_state_get_orientation(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec3 gvr_controller_state_get_position(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec3 gvr_controller_state_get_gyro(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec3 gvr_controller_state_get_accel(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_is_touching(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern gvr_vec2 gvr_controller_state_get_touch_pos(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_touch_down(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_touch_up(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_recentered(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_button_state(IntPtr state, int button);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_button_down(IntPtr state, int button);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_button_up(IntPtr state, int button);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_orientation_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_gyro_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_accel_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_touch_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_button_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern byte gvr_controller_state_get_battery_charging(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_state_get_battery_level(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern long gvr_controller_state_get_last_battery_timestamp(IntPtr state);
|
||||
|
||||
[DllImport(dllName)]
|
||||
private static extern int gvr_controller_get_count(IntPtr api);
|
||||
|
||||
private const string VRCORE_UTILS_CLASS = "com.google.vr.vrcore.base.api.VrCoreUtils";
|
||||
|
||||
private IntPtr api;
|
||||
private bool hasBatteryMethods = false;
|
||||
|
||||
private AndroidJavaObject androidContext;
|
||||
private AndroidJavaObject classLoader;
|
||||
|
||||
private bool error = false;
|
||||
private string errorDetails = string.Empty;
|
||||
|
||||
private IntPtr statePtr;
|
||||
|
||||
private MutablePose3D pose3d = new MutablePose3D();
|
||||
|
||||
private GvrControllerButton[] lastButtonsState = new GvrControllerButton[2];
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return hasBatteryMethods; }
|
||||
}
|
||||
|
||||
public int MaxControllerCount {
|
||||
get {
|
||||
if (api == IntPtr.Zero) {
|
||||
return 0;
|
||||
}
|
||||
return gvr_controller_get_count(api);
|
||||
}
|
||||
}
|
||||
|
||||
internal AndroidNativeControllerProvider() {
|
||||
// Debug.Log("Initializing Daydream controller API.");
|
||||
|
||||
int options = gvr_controller_get_default_options();
|
||||
options |= GVR_CONTROLLER_ENABLE_ACCEL;
|
||||
options |= GVR_CONTROLLER_ENABLE_GYRO;
|
||||
options |= GVR_CONTROLLER_ENABLE_POSITION;
|
||||
|
||||
statePtr = gvr_controller_state_create();
|
||||
// Get a hold of the activity, context and class loader.
|
||||
AndroidJavaObject activity = GvrActivityHelper.GetActivity();
|
||||
if (activity == null) {
|
||||
error = true;
|
||||
errorDetails = "Failed to get Activity from Unity Player.";
|
||||
return;
|
||||
}
|
||||
androidContext = GvrActivityHelper.GetApplicationContext(activity);
|
||||
if (androidContext == null) {
|
||||
error = true;
|
||||
errorDetails = "Failed to get Android application context from Activity.";
|
||||
return;
|
||||
}
|
||||
classLoader = GetClassLoaderFromActivity(activity);
|
||||
if (classLoader == null) {
|
||||
error = true;
|
||||
errorDetails = "Failed to get class loader from Activity.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Use IntPtr instead of GetRawObject() so that Unity can shut down gracefully on
|
||||
// Application.Quit(). Note that GetRawObject() is not pinned by the receiver so it's not
|
||||
// cleaned up appropriately on shutdown, which is a known bug in Unity.
|
||||
IntPtr androidContextPtr = AndroidJNI.NewLocalRef(androidContext.GetRawObject());
|
||||
IntPtr classLoaderPtr = AndroidJNI.NewLocalRef(classLoader.GetRawObject());
|
||||
Debug.Log ("Creating and initializing GVR API controller object.");
|
||||
api = gvr_controller_create_and_init_android (IntPtr.Zero, androidContextPtr, classLoaderPtr,
|
||||
options, IntPtr.Zero);
|
||||
AndroidJNI.DeleteLocalRef(androidContextPtr);
|
||||
AndroidJNI.DeleteLocalRef(classLoaderPtr);
|
||||
if (IntPtr.Zero == api) {
|
||||
Debug.LogError("Error creating/initializing Daydream controller API.");
|
||||
error = true;
|
||||
errorDetails = "Failed to initialize Daydream controller API.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
gvr_controller_state_get_battery_charging(statePtr);
|
||||
gvr_controller_state_get_battery_level(statePtr);
|
||||
hasBatteryMethods = true;
|
||||
} catch (EntryPointNotFoundException) {
|
||||
// Older VrCore version. Does not support battery indicator.
|
||||
// Note that controller API is not dynamically loaded as of June 2017 (b/35662043),
|
||||
// so we'll need to support this case indefinitely...
|
||||
}
|
||||
|
||||
// Debug.Log("GVR API successfully initialized. Now resuming it.");
|
||||
gvr_controller_resume(api);
|
||||
// Debug.Log("GVR API resumed.");
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
if (disposing) {
|
||||
// Debug.Log("Destroying GVR API structures.");
|
||||
gvr_controller_state_destroy(ref statePtr);
|
||||
gvr_controller_destroy(ref api);
|
||||
if (statePtr != IntPtr.Zero) {
|
||||
Debug.LogError("gvr_controller_state not zeroed after destroy");
|
||||
}
|
||||
if (api != IntPtr.Zero) {
|
||||
Debug.LogError("gvr_controller_api not zeroed after destroy");
|
||||
}
|
||||
// Debug.Log("AndroidNativeControllerProvider destroyed.");
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (error) {
|
||||
outState.connectionState = GvrConnectionState.Error;
|
||||
outState.apiStatus = GvrControllerApiStatus.Error;
|
||||
outState.errorDetails = errorDetails;
|
||||
return;
|
||||
}
|
||||
if (api == IntPtr.Zero || statePtr == IntPtr.Zero) {
|
||||
Debug.LogError("AndroidNativeControllerProvider used after dispose.");
|
||||
return;
|
||||
}
|
||||
gvr_controller_state_update(api, controller_id, statePtr);
|
||||
|
||||
outState.connectionState = ConvertConnectionState(
|
||||
gvr_controller_state_get_connection_state(statePtr));
|
||||
outState.apiStatus = ConvertControllerApiStatus(
|
||||
gvr_controller_state_get_api_status(statePtr));
|
||||
|
||||
gvr_quat rawOri = gvr_controller_state_get_orientation(statePtr);
|
||||
gvr_vec3 rawAccel = gvr_controller_state_get_accel(statePtr);
|
||||
gvr_vec3 rawGyro = gvr_controller_state_get_gyro(statePtr);
|
||||
gvr_vec3 rawPos = gvr_controller_state_get_position(statePtr);
|
||||
|
||||
// Convert GVR API orientation (right-handed) into Unity axis system (left-handed).
|
||||
pose3d.Set(new Vector3(rawPos.x,rawPos.y,rawPos.z), new Quaternion(rawOri.x, rawOri.y, rawOri.z, rawOri.w));
|
||||
pose3d.SetRightHanded(pose3d.Matrix);
|
||||
outState.orientation = pose3d.Orientation;
|
||||
outState.position = pose3d.Position;
|
||||
|
||||
// For accelerometer, we have to flip Z because the GVR API has Z pointing backwards
|
||||
// and Unity has Z pointing forward.
|
||||
outState.accel = new Vector3(rawAccel.x, rawAccel.y, -rawAccel.z);
|
||||
|
||||
// Gyro in GVR represents a right-handed angular velocity about each axis (positive means
|
||||
// clockwise when sighting along axis). Since Unity uses a left-handed system, we flip the
|
||||
// signs to adjust the sign of the rotational velocity (so that positive means
|
||||
// counter-clockwise). In addition, since in Unity the Z axis points forward while GVR
|
||||
// has Z pointing backwards, we flip the Z axis sign again. So the result is that
|
||||
// we should use -X, -Y, +Z:
|
||||
outState.gyro = new Vector3(-rawGyro.x, -rawGyro.y, rawGyro.z);
|
||||
|
||||
gvr_vec2 touchPos = gvr_controller_state_get_touch_pos(statePtr);
|
||||
outState.touchPos = new Vector2(touchPos.x, touchPos.y);
|
||||
|
||||
outState.buttonsState = 0;
|
||||
for (int i=0; i<GVR_BUTTONS.Length; i++) {
|
||||
if (0 != gvr_controller_state_get_button_state(statePtr, GVR_BUTTONS[i])) {
|
||||
outState.buttonsState |= GVR_UNITY_BUTTONS[i];
|
||||
}
|
||||
}
|
||||
if (0 != gvr_controller_state_is_touching(statePtr)) {
|
||||
outState.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
}
|
||||
|
||||
outState.SetButtonsUpDownFromPrevious(lastButtonsState[controller_id]);
|
||||
lastButtonsState[controller_id] = outState.buttonsState;
|
||||
|
||||
outState.recentered = 0 != gvr_controller_state_get_recentered(statePtr);
|
||||
outState.gvrPtr = statePtr;
|
||||
|
||||
if (hasBatteryMethods) {
|
||||
outState.isCharging = 0 != gvr_controller_state_get_battery_charging(statePtr);
|
||||
outState.batteryLevel = (GvrControllerBatteryLevel)gvr_controller_state_get_battery_level(statePtr);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPause() {
|
||||
if (IntPtr.Zero != api) {
|
||||
gvr_controller_pause(api);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnResume() {
|
||||
if (IntPtr.Zero != api) {
|
||||
gvr_controller_resume(api);
|
||||
}
|
||||
}
|
||||
|
||||
private GvrConnectionState ConvertConnectionState(int connectionState) {
|
||||
switch (connectionState) {
|
||||
case GVR_CONTROLLER_CONNECTED:
|
||||
return GvrConnectionState.Connected;
|
||||
case GVR_CONTROLLER_CONNECTING:
|
||||
return GvrConnectionState.Connecting;
|
||||
case GVR_CONTROLLER_SCANNING:
|
||||
return GvrConnectionState.Scanning;
|
||||
default:
|
||||
return GvrConnectionState.Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
private GvrControllerApiStatus ConvertControllerApiStatus(int gvrControllerApiStatus) {
|
||||
switch (gvrControllerApiStatus) {
|
||||
case GVR_CONTROLLER_API_OK:
|
||||
return GvrControllerApiStatus.Ok;
|
||||
case GVR_CONTROLLER_API_UNSUPPORTED:
|
||||
return GvrControllerApiStatus.Unsupported;
|
||||
case GVR_CONTROLLER_API_NOT_AUTHORIZED:
|
||||
return GvrControllerApiStatus.NotAuthorized;
|
||||
case GVR_CONTROLLER_API_SERVICE_OBSOLETE:
|
||||
return GvrControllerApiStatus.ApiServiceObsolete;
|
||||
case GVR_CONTROLLER_API_CLIENT_OBSOLETE:
|
||||
return GvrControllerApiStatus.ApiClientObsolete;
|
||||
case GVR_CONTROLLER_API_MALFUNCTION:
|
||||
return GvrControllerApiStatus.ApiMalfunction;
|
||||
case GVR_CONTROLLER_API_UNAVAILABLE:
|
||||
default: // Fall through.
|
||||
return GvrControllerApiStatus.Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateInputEvents(bool currentState, ref bool previousState, ref bool up, ref bool down) {
|
||||
|
||||
down = !previousState && currentState;
|
||||
up = previousState && !currentState;
|
||||
|
||||
previousState = currentState;
|
||||
}
|
||||
|
||||
private static AndroidJavaObject GetClassLoaderFromActivity(AndroidJavaObject activity) {
|
||||
AndroidJavaObject result = activity.Call<AndroidJavaObject>("getClassLoader");
|
||||
if (result == null) {
|
||||
Debug.LogErrorFormat("Failed to get class loader from Activity.");
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int GetVrCoreClientApiVersion(AndroidJavaObject activity) {
|
||||
try {
|
||||
AndroidJavaClass utilsClass = new AndroidJavaClass(VRCORE_UTILS_CLASS);
|
||||
int apiVersion = utilsClass.CallStatic<int>("getVrCoreClientApiVersion", activity);
|
||||
// Debug.LogFormat("VrCore client API version: " + apiVersion);
|
||||
return apiVersion;
|
||||
} catch (Exception exc) {
|
||||
// Even though a catch-all block is normally frowned upon, in this case we really
|
||||
// need it because this method has to be robust to unpredictable circumstances:
|
||||
// VrCore might not exist in the device, the Java layer might be broken, etc, etc.
|
||||
// None of those should abort the app.
|
||||
Debug.LogError("Error obtaining VrCore client API version: " + exc);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
#endif // UNITY_ANDROID && !UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fda152dc25154b4a9cccb75fd77f018
|
||||
timeCreated: 1462060442
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Gvr;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Dummy controller provider.
|
||||
/// Used in platforms that do not support controllers.
|
||||
class DummyControllerProvider : IControllerProvider {
|
||||
private ControllerState dummyState = new ControllerState();
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return false; }
|
||||
}
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
internal DummyControllerProvider() {}
|
||||
public void Dispose() {}
|
||||
public void ReadState(ControllerState outState,int controller_id) {
|
||||
outState.CopyFrom(dummyState);
|
||||
}
|
||||
public void OnPause() {}
|
||||
public void OnResume() {}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f791be37caef48c79f72011276ab16a
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This provider is only available in the editor.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using Gvr;
|
||||
|
||||
namespace Gvr.Internal {
|
||||
/// Controller provider used when playing in the Unity Editor.
|
||||
/// Supports the Controller Emulator and Mouse input to mock the controller.
|
||||
class EditorControllerProvider : IControllerProvider {
|
||||
private EmulatorControllerProvider emulatorControllerProvider;
|
||||
private MouseControllerProvider mouseControllerProvider;
|
||||
#if UNITY_HAS_GOOGLEVR
|
||||
/// Helper class to get Instant Preview controller events if connected.
|
||||
private InstantPreviewControllerProvider instantPreviewControllerProvider =
|
||||
new InstantPreviewControllerProvider();
|
||||
#endif // UNITY_HAS_GOOGLEVR
|
||||
|
||||
ControllerState emulatorState = new ControllerState();
|
||||
ControllerState mouseState = new ControllerState();
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return emulatorControllerProvider.SupportsBatteryStatus; }
|
||||
}
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
internal EditorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
|
||||
emulatorControllerProvider = new EmulatorControllerProvider(connectionMode);
|
||||
mouseControllerProvider = new MouseControllerProvider();
|
||||
}
|
||||
|
||||
public void Dispose() {}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (controller_id != 0) {
|
||||
return;
|
||||
}
|
||||
#if UNITY_HAS_GOOGLEVR
|
||||
if (InstantPreview.Instance != null
|
||||
&& InstantPreview.Instance.IsCurrentlyConnected
|
||||
&& !EmulatorManager.Instance.Connected) {
|
||||
// Uses Instant Preview to get controller state if connected.
|
||||
instantPreviewControllerProvider.ReadState(outState);
|
||||
return;
|
||||
}
|
||||
#endif // UNITY_HAS_GOOGLEVR
|
||||
|
||||
// If Instant Preview is not connected, tries to use the emulator or
|
||||
// mouse.
|
||||
emulatorControllerProvider.ReadState(emulatorState, controller_id);
|
||||
mouseControllerProvider.ReadState(mouseState, controller_id);
|
||||
|
||||
// Defaults to mouse state if the emulator isn't available.
|
||||
if (emulatorState.connectionState != GvrConnectionState.Connected
|
||||
&& mouseState.connectionState == GvrConnectionState.Connected) {
|
||||
outState.CopyFrom(mouseState);
|
||||
} else {
|
||||
outState.CopyFrom(emulatorState);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPause() {
|
||||
emulatorControllerProvider.OnPause();
|
||||
mouseControllerProvider.OnPause();
|
||||
}
|
||||
|
||||
public void OnResume() {
|
||||
emulatorControllerProvider.OnResume();
|
||||
mouseControllerProvider.OnResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0a32f2191dec4e1b8e05acab6002be6
|
||||
timeCreated: 1496354837
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,191 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This class is only used in the Editor, so make sure to only compile it on that platform.
|
||||
// Additionally, it depends on EmulatorManager which is only compiled in the editor.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Controller provider that connects to the controller emulator to obtain controller events.
|
||||
class EmulatorControllerProvider : IControllerProvider {
|
||||
private ControllerState state = new ControllerState();
|
||||
|
||||
/// Yaw correction due to recentering.
|
||||
private Quaternion yawCorrection = Quaternion.identity;
|
||||
|
||||
/// True if we performed the initial recenter.
|
||||
private bool initialRecenterDone = false;
|
||||
|
||||
/// The last (uncorrected) orientation received from the emulator.
|
||||
private Quaternion lastRawOrientation = Quaternion.identity;
|
||||
private GvrControllerButton lastButtonsState;
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return true; }
|
||||
}
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
/// Creates a new EmulatorControllerProvider with the specified settings.
|
||||
internal EmulatorControllerProvider(GvrControllerInput.EmulatorConnectionMode connectionMode) {
|
||||
if (connectionMode == GvrControllerInput.EmulatorConnectionMode.USB) {
|
||||
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.USB;
|
||||
} else if (connectionMode == GvrControllerInput.EmulatorConnectionMode.WIFI) {
|
||||
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.WIFI;
|
||||
} else {
|
||||
EmulatorConfig.Instance.PHONE_EVENT_MODE = EmulatorConfig.Mode.OFF;
|
||||
}
|
||||
|
||||
EmulatorManager.Instance.touchEventListeners += HandleTouchEvent;
|
||||
EmulatorManager.Instance.orientationEventListeners += HandleOrientationEvent;
|
||||
EmulatorManager.Instance.buttonEventListeners += HandleButtonEvent;
|
||||
EmulatorManager.Instance.gyroEventListeners += HandleGyroEvent;
|
||||
EmulatorManager.Instance.accelEventListeners += HandleAccelEvent;
|
||||
}
|
||||
|
||||
public void Dispose() {}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (controller_id != 0) {
|
||||
return;
|
||||
}
|
||||
lock (state) {
|
||||
state.connectionState = GvrConnectionState.Connected;
|
||||
if (!EmulatorManager.Instance.Connected) {
|
||||
state.connectionState = EmulatorManager.Instance.Connecting ?
|
||||
GvrConnectionState.Connecting : GvrConnectionState.Disconnected;
|
||||
}
|
||||
state.apiStatus = EmulatorManager.Instance.Connected ? GvrControllerApiStatus.Ok :
|
||||
GvrControllerApiStatus.Unavailable;
|
||||
|
||||
// During emulation, just assume the controller is fully charged
|
||||
state.isCharging = false;
|
||||
state.batteryLevel = GvrControllerBatteryLevel.Full;
|
||||
|
||||
state.SetButtonsUpDownFromPrevious(lastButtonsState);
|
||||
lastButtonsState = state.buttonsState;
|
||||
|
||||
outState.CopyFrom(state);
|
||||
}
|
||||
state.ClearTransientState();
|
||||
}
|
||||
|
||||
public void OnPause() {}
|
||||
public void OnResume() {}
|
||||
|
||||
private void HandleTouchEvent(EmulatorTouchEvent touchEvent) {
|
||||
if (touchEvent.pointers.Count < 1) return;
|
||||
EmulatorTouchEvent.Pointer pointer = touchEvent.pointers[0];
|
||||
|
||||
lock (state) {
|
||||
state.touchPos = new Vector2(pointer.normalizedX, pointer.normalizedY);
|
||||
switch (touchEvent.getActionMasked()) {
|
||||
case EmulatorTouchEvent.Action.kActionDown:
|
||||
state.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
break;
|
||||
case EmulatorTouchEvent.Action.kActionMove:
|
||||
state.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
break;
|
||||
case EmulatorTouchEvent.Action.kActionUp:
|
||||
state.buttonsState &= ~GvrControllerButton.TouchPadTouch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleOrientationEvent(EmulatorOrientationEvent orientationEvent) {
|
||||
lastRawOrientation = ConvertEmulatorQuaternion(orientationEvent.orientation);
|
||||
if (!initialRecenterDone) {
|
||||
Recenter();
|
||||
initialRecenterDone = true;
|
||||
}
|
||||
lock (state) {
|
||||
state.orientation = yawCorrection * lastRawOrientation;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleButtonEvent(EmulatorButtonEvent buttonEvent) {
|
||||
GvrControllerButton buttonMask = 0;
|
||||
switch (buttonEvent.code) {
|
||||
case EmulatorButtonEvent.ButtonCode.kApp:
|
||||
buttonMask = GvrControllerButton.App;
|
||||
break;
|
||||
case EmulatorButtonEvent.ButtonCode.kHome:
|
||||
buttonMask = GvrControllerButton.System;
|
||||
break;
|
||||
case EmulatorButtonEvent.ButtonCode.kClick:
|
||||
buttonMask = GvrControllerButton.TouchPadButton;
|
||||
break;
|
||||
}
|
||||
if (buttonMask != 0) {
|
||||
lock (state) {
|
||||
state.buttonsState &= ~buttonMask;
|
||||
if (buttonEvent.down) {
|
||||
state.buttonsState |= buttonMask;
|
||||
}
|
||||
}
|
||||
if (buttonMask == GvrControllerButton.System) {
|
||||
if (!buttonEvent.down) {
|
||||
// Finished the recentering gesture. Recenter controller.
|
||||
Recenter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleGyroEvent(EmulatorGyroEvent gyroEvent) {
|
||||
lock (state) {
|
||||
state.gyro = ConvertEmulatorGyro(gyroEvent.value);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAccelEvent(EmulatorAccelEvent accelEvent) {
|
||||
lock (state) {
|
||||
state.accel = ConvertEmulatorAccel(accelEvent.value);
|
||||
}
|
||||
}
|
||||
|
||||
private static Quaternion ConvertEmulatorQuaternion(Quaternion emulatorQuat) {
|
||||
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
|
||||
return new Quaternion(emulatorQuat.x, -emulatorQuat.z, emulatorQuat.y, emulatorQuat.w);
|
||||
}
|
||||
|
||||
private static Vector3 ConvertEmulatorGyro(Vector3 emulatorGyro) {
|
||||
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
|
||||
return new Vector3(-emulatorGyro.x, -emulatorGyro.z, -emulatorGyro.y);
|
||||
}
|
||||
|
||||
private static Vector3 ConvertEmulatorAccel(Vector3 emulatorAccel) {
|
||||
// Convert from the emulator's coordinate space to Unity's standard coordinate space.
|
||||
return new Vector3(emulatorAccel.x, emulatorAccel.z, emulatorAccel.y);
|
||||
}
|
||||
|
||||
private void Recenter() {
|
||||
lock (state) {
|
||||
// We want the current orientation to be "forward" so, we set the yaw correction
|
||||
// to undo the current rotation's yaw.
|
||||
yawCorrection = Quaternion.AngleAxis(-lastRawOrientation.eulerAngles.y, Vector3.up);
|
||||
state.orientation = Quaternion.identity;
|
||||
state.recentered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0ca644865f5f4479fb50471605078cf0
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,198 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Gvr;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Gvr.Internal {
|
||||
/// Mocks controller input by using the mouse.
|
||||
/// The controller is connected when holding left shift.
|
||||
/// Move the mouse to control gyroscope and orientation.
|
||||
/// The left mouse button is used for the clickButton.
|
||||
/// The right mouse button is used for the appButton.
|
||||
/// The middle mouse button is used for the homeButton.
|
||||
class MouseControllerProvider : IControllerProvider {
|
||||
private const string AXIS_MOUSE_X = "Mouse X";
|
||||
private const string AXIS_MOUSE_Y = "Mouse Y";
|
||||
|
||||
private ControllerState state = new ControllerState();
|
||||
|
||||
private Vector2 mouseDelta = new Vector2();
|
||||
|
||||
/// Need to store the state of the buttons from the previous frame.
|
||||
/// This is because Input.GetMouseButtonDown and Input.GetMouseButtonUp
|
||||
/// don't work when called after WaitForEndOfFrame, which is when ReadState is called.
|
||||
private bool wasTouching;
|
||||
private GvrControllerButton lastButtonsState;
|
||||
|
||||
private const float ROTATE_SENSITIVITY = 4.5f;
|
||||
private const float TOUCH_SENSITIVITY = .12f;
|
||||
private static readonly Vector3 INVERT_Y = new Vector3(1, -1, 1);
|
||||
|
||||
public static bool IsMouseAvailable {
|
||||
get {
|
||||
return Input.mousePresent && IsActivateButtonPressed;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsActivateButtonPressed {
|
||||
get {
|
||||
return Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsClickButtonPressed {
|
||||
get {
|
||||
return Input.GetMouseButton(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsAppButtonPressed {
|
||||
get {
|
||||
return Input.GetMouseButton(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsHomeButtonPressed {
|
||||
get {
|
||||
return Input.GetMouseButton(2);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsTouching {
|
||||
get {
|
||||
return Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SupportsBatteryStatus {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public int MaxControllerCount {
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
internal MouseControllerProvider() {}
|
||||
|
||||
public void Dispose() {}
|
||||
|
||||
public void ReadState(ControllerState outState, int controller_id) {
|
||||
if (controller_id != 0) {
|
||||
return;
|
||||
}
|
||||
lock (state) {
|
||||
UpdateState();
|
||||
|
||||
outState.CopyFrom(state);
|
||||
}
|
||||
state.ClearTransientState();
|
||||
}
|
||||
|
||||
public void OnPause() {}
|
||||
public void OnResume() {}
|
||||
|
||||
private void UpdateState() {
|
||||
GvrCursorHelper.ControllerEmulationActive = IsMouseAvailable;
|
||||
|
||||
if (!IsMouseAvailable) {
|
||||
ClearState();
|
||||
return;
|
||||
}
|
||||
|
||||
state.connectionState = GvrConnectionState.Connected;
|
||||
state.apiStatus = GvrControllerApiStatus.Ok;
|
||||
state.isCharging = false;
|
||||
state.batteryLevel = GvrControllerBatteryLevel.Full;
|
||||
|
||||
UpdateButtonStates();
|
||||
|
||||
mouseDelta.Set(
|
||||
Input.GetAxis(AXIS_MOUSE_X),
|
||||
Input.GetAxis(AXIS_MOUSE_Y)
|
||||
);
|
||||
|
||||
if (0 != (state.buttonsState & GvrControllerButton.TouchPadTouch)) {
|
||||
UpdateTouchPos();
|
||||
} else {
|
||||
UpdateOrientation();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTouchPos() {
|
||||
Vector3 currentMousePosition = Input.mousePosition;
|
||||
Vector2 touchDelta = mouseDelta * TOUCH_SENSITIVITY;
|
||||
touchDelta.y *= -1.0f;
|
||||
|
||||
state.touchPos += touchDelta;
|
||||
state.touchPos.x = Mathf.Clamp01(state.touchPos.x);
|
||||
state.touchPos.y = Mathf.Clamp01(state.touchPos.y);
|
||||
}
|
||||
|
||||
private void UpdateOrientation() {
|
||||
Vector3 deltaDegrees = Vector3.Scale(mouseDelta, INVERT_Y) * ROTATE_SENSITIVITY;
|
||||
|
||||
state.gyro = deltaDegrees * (Mathf.Deg2Rad / Time.deltaTime);
|
||||
|
||||
Quaternion yaw = Quaternion.AngleAxis(deltaDegrees.x, Vector3.up);
|
||||
Quaternion pitch = Quaternion.AngleAxis(deltaDegrees.y, Vector3.right);
|
||||
state.orientation = state.orientation * yaw * pitch;
|
||||
}
|
||||
|
||||
private void UpdateButtonStates() {
|
||||
state.buttonsState = 0;
|
||||
if (IsClickButtonPressed) {
|
||||
state.buttonsState |= GvrControllerButton.TouchPadButton;
|
||||
}
|
||||
if (IsAppButtonPressed) {
|
||||
state.buttonsState |= GvrControllerButton.App;
|
||||
}
|
||||
if (IsHomeButtonPressed) {
|
||||
state.buttonsState |= GvrControllerButton.System;
|
||||
}
|
||||
if (IsTouching) {
|
||||
state.buttonsState |= GvrControllerButton.TouchPadTouch;
|
||||
}
|
||||
|
||||
state.SetButtonsUpDownFromPrevious(lastButtonsState);
|
||||
lastButtonsState = state.buttonsState;
|
||||
|
||||
if (0 != (state.buttonsUp & GvrControllerButton.TouchPadTouch)) {
|
||||
ClearTouchPos();
|
||||
}
|
||||
|
||||
if (0 != (state.buttonsUp & GvrControllerButton.System)) {
|
||||
Recenter();
|
||||
}
|
||||
}
|
||||
|
||||
private void Recenter() {
|
||||
Quaternion yawCorrection = Quaternion.AngleAxis(-state.orientation.eulerAngles.y, Vector3.up);
|
||||
state.orientation = state.orientation * yawCorrection;
|
||||
state.recentered = true;
|
||||
}
|
||||
|
||||
private void ClearTouchPos() {
|
||||
state.touchPos = new Vector2(0.5f, 0.5f);
|
||||
}
|
||||
|
||||
private void ClearState() {
|
||||
state.connectionState = GvrConnectionState.Disconnected;
|
||||
state.buttonsState = 0;
|
||||
state.buttonsDown = 0;
|
||||
state.buttonsUp = 0;
|
||||
ClearTouchPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8b9c789363a44b1e87727b1c4d085f6
|
||||
timeCreated: 1496352344
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
using Gvr;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Internal representation of the controller's current state.
|
||||
/// This representation is used by controller providers to represent the controller's state.
|
||||
///
|
||||
/// The fields in this class have identical meanings to their correspondents in the GVR C API,
|
||||
/// so they are not redundantly documented here.
|
||||
class ControllerState {
|
||||
internal GvrConnectionState connectionState = GvrConnectionState.Disconnected;
|
||||
internal GvrControllerApiStatus apiStatus = GvrControllerApiStatus.Unavailable;
|
||||
internal Quaternion orientation = Quaternion.identity;
|
||||
internal Vector3 position = Vector3.zero;
|
||||
internal Vector3 gyro = Vector3.zero;
|
||||
internal Vector3 accel = Vector3.zero;
|
||||
internal Vector2 touchPos = Vector2.zero;
|
||||
internal bool recentered = false;
|
||||
|
||||
internal GvrControllerButton buttonsState;
|
||||
internal GvrControllerButton buttonsDown;
|
||||
internal GvrControllerButton buttonsUp;
|
||||
|
||||
internal string errorDetails = "";
|
||||
internal IntPtr gvrPtr = IntPtr.Zero;
|
||||
|
||||
internal bool isCharging = false;
|
||||
internal GvrControllerBatteryLevel batteryLevel = GvrControllerBatteryLevel.Unknown;
|
||||
|
||||
public void CopyFrom(ControllerState other) {
|
||||
connectionState = other.connectionState;
|
||||
apiStatus = other.apiStatus;
|
||||
orientation = other.orientation;
|
||||
position = other.position;
|
||||
gyro = other.gyro;
|
||||
accel = other.accel;
|
||||
touchPos = other.touchPos;
|
||||
recentered = other.recentered;
|
||||
buttonsState = other.buttonsState;
|
||||
buttonsDown = other.buttonsDown;
|
||||
buttonsUp = other.buttonsUp;
|
||||
errorDetails = other.errorDetails;
|
||||
gvrPtr = other.gvrPtr;
|
||||
isCharging = other.isCharging;
|
||||
batteryLevel = other.batteryLevel;
|
||||
}
|
||||
|
||||
/// Resets the transient state (the state variables that represent events, and which are true
|
||||
/// for only one frame).
|
||||
public void ClearTransientState() {
|
||||
recentered = false;
|
||||
buttonsState = 0;
|
||||
buttonsDown = 0;
|
||||
buttonsUp = 0;
|
||||
}
|
||||
|
||||
public void SetButtonsUpDownFromPrevious(GvrControllerButton prevButtonsState) {
|
||||
buttonsDown = ~prevButtonsState & buttonsState;
|
||||
buttonsUp = prevButtonsState & ~buttonsState;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9159532a8a3d946aa9df74e771243e5b
|
||||
timeCreated: 1462043669
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2018 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
using Gvr;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
class ControllerUtils {
|
||||
|
||||
/// Convenience array of all hands.
|
||||
public static GvrControllerHand[] AllHands = {
|
||||
GvrControllerHand.Right,
|
||||
GvrControllerHand.Left,
|
||||
};
|
||||
|
||||
/// Returns true while the user holds down any of buttons specified in `buttons` on
|
||||
/// any controller.
|
||||
public static bool AnyButton(GvrControllerButton buttons) {
|
||||
bool ret = false;
|
||||
foreach (var hand in AllHands) {
|
||||
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
|
||||
ret |= device.GetButton(buttons);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns true in the frame the user starts pressing down any of buttons specified
|
||||
/// in `buttons` on any controller.
|
||||
public static bool AnyButtonDown(GvrControllerButton buttons) {
|
||||
bool ret = false;
|
||||
foreach (var hand in AllHands) {
|
||||
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
|
||||
ret |= device.GetButtonDown(buttons);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Returns true the frame after the user stops pressing down any of buttons specified
|
||||
/// in `buttons` on any controller.
|
||||
public static bool AnyButtonUp(GvrControllerButton buttons) {
|
||||
bool ret = false;
|
||||
foreach (var hand in AllHands) {
|
||||
GvrControllerInputDevice device = GvrControllerInput.GetDevice(hand);
|
||||
ret |= device.GetButtonUp(buttons);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8358cf789a848412fbb10ba05c4f3fce
|
||||
timeCreated: 1524508137
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c82eb0d7ee68d344bd339cec427a446
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,258 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This class is only used in the Editor, so make sure to only compile it on that platform.
|
||||
// Additionally, If this class is compiled on Android then Unity will insert the INTERNET permission
|
||||
// into the manifest because of the reference to the type TCPClient. Excluding this class in the android
|
||||
// build ensures that it is only included if the developer using the SDK actually uses INTERNET related services.
|
||||
// This MonoBehaviour is only ever instantiated dynamically, so it is fine that it is only compiled in the Editor,
|
||||
// Otherwise it would cause serialization issues.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
using proto;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
|
||||
public enum EmulatorClientSocketConnectionState {
|
||||
Disconnected = 0,
|
||||
Connecting = 1,
|
||||
Connected = 2,
|
||||
};
|
||||
|
||||
class EmulatorClientSocket : MonoBehaviour {
|
||||
private static readonly int kPhoneEventPort = 7003;
|
||||
private const int kSocketReadTimeoutMillis = 5000;
|
||||
|
||||
// Minimum interval, in seconds, between attempts to reconnect the socket.
|
||||
private const float kMinReconnectInterval = 1f;
|
||||
|
||||
private TcpClient phoneMirroringSocket;
|
||||
|
||||
private Thread phoneEventThread;
|
||||
|
||||
private volatile bool shouldStop = false;
|
||||
|
||||
// Flag used to limit connection state logging to initial failure and successful reconnects.
|
||||
private volatile bool lastConnectionAttemptWasSuccessful = true;
|
||||
|
||||
private EmulatorManager phoneRemote;
|
||||
public EmulatorClientSocketConnectionState connected { get; private set; }
|
||||
|
||||
public void Init(EmulatorManager remote) {
|
||||
phoneRemote = remote;
|
||||
|
||||
if (EmulatorConfig.Instance.PHONE_EVENT_MODE != EmulatorConfig.Mode.OFF) {
|
||||
phoneEventThread = new Thread(phoneEventSocketLoop);
|
||||
phoneEventThread.IsBackground = true;
|
||||
phoneEventThread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void phoneEventSocketLoop() {
|
||||
while (!shouldStop) {
|
||||
long lastConnectionAttemptTime = DateTime.Now.Ticks;
|
||||
try {
|
||||
phoneConnect();
|
||||
} catch(Exception e) {
|
||||
if (lastConnectionAttemptWasSuccessful) {
|
||||
Debug.LogWarningFormat("{0}\n{1}", e.Message, e.StackTrace);
|
||||
// Suppress additional failures until we have successfully reconnected.
|
||||
lastConnectionAttemptWasSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a while in order to enforce the minimum time between connection attempts.
|
||||
TimeSpan elapsed = new TimeSpan(DateTime.Now.Ticks - lastConnectionAttemptTime);
|
||||
float toWait = kMinReconnectInterval - (float) elapsed.TotalSeconds;
|
||||
if (toWait > 0) {
|
||||
Thread.Sleep((int) (toWait * 1000));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void phoneConnect() {
|
||||
string addr = EmulatorConfig.Instance.PHONE_EVENT_MODE == EmulatorConfig.Mode.USB
|
||||
? EmulatorConfig.USB_SERVER_IP : EmulatorConfig.WIFI_SERVER_IP;
|
||||
|
||||
try {
|
||||
if (EmulatorConfig.Instance.PHONE_EVENT_MODE == EmulatorConfig.Mode.USB) {
|
||||
setupPortForwarding(kPhoneEventPort);
|
||||
}
|
||||
TcpClient tcpClient = new TcpClient(addr, kPhoneEventPort);
|
||||
connected = EmulatorClientSocketConnectionState.Connecting;
|
||||
ProcessConnection(tcpClient);
|
||||
tcpClient.Close();
|
||||
} finally {
|
||||
connected = EmulatorClientSocketConnectionState.Disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
private void setupPortForwarding(int port) {
|
||||
#if !UNITY_WEBPLAYER
|
||||
string adbCommand = string.Format("adb forward tcp:{0} tcp:{0}", port);
|
||||
System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
|
||||
string processFilename;
|
||||
string processArguments;
|
||||
int kExitCodeCommandNotFound;
|
||||
|
||||
if (Application.platform == RuntimePlatform.WindowsEditor ||
|
||||
Application.platform == RuntimePlatform.WindowsPlayer) {
|
||||
processFilename = "CMD.exe";
|
||||
processArguments = @"/k " + adbCommand + " & exit";
|
||||
// See "Common Error Lookup Tool" (https://www.microsoft.com/en-us/download/details.aspx?id=985)
|
||||
// MSG_DIR_BAD_COMMAND_OR_FILE (cmdmsg.h)
|
||||
kExitCodeCommandNotFound = 9009; // 0x2331
|
||||
} else { // Unix
|
||||
processFilename = "bash";
|
||||
processArguments = string.Format("-l -c \"{0}\"", adbCommand);
|
||||
// "command not found" (see http://tldp.org/LDP/abs/html/exitcodes.html)
|
||||
kExitCodeCommandNotFound = 127;
|
||||
}
|
||||
|
||||
System.Diagnostics.ProcessStartInfo myProcessStartInfo =
|
||||
new System.Diagnostics.ProcessStartInfo(processFilename, processArguments);
|
||||
myProcessStartInfo.UseShellExecute = false;
|
||||
myProcessStartInfo.RedirectStandardError = true;
|
||||
myProcessStartInfo.CreateNoWindow = true;
|
||||
myProcess.StartInfo = myProcessStartInfo;
|
||||
myProcess.Start();
|
||||
myProcess.WaitForExit();
|
||||
// Also wait for HasExited here, to avoid ExitCode access below occasionally throwing InvalidOperationException
|
||||
while (!myProcess.HasExited) {
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
int exitCode = myProcess.ExitCode;
|
||||
string standardError = myProcess.StandardError.ReadToEnd();
|
||||
myProcess.Close();
|
||||
|
||||
if (exitCode == 0) {
|
||||
// Port forwarding setup successfully.
|
||||
return;
|
||||
}
|
||||
|
||||
if (exitCode == kExitCodeCommandNotFound) {
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
"Android Debug Bridge (`adb`) command not found." +
|
||||
"\nVerify that the Android SDK is installed and that the directory containing" +
|
||||
" `adb` is included in your PATH environment variable.");
|
||||
}
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"Failed to setup port forwarding." +
|
||||
" Exit code {0} returned by process: {1} {2}\n{3}",
|
||||
exitCode, processFilename, processArguments, standardError));
|
||||
#endif // !UNITY_WEBPLAYER
|
||||
}
|
||||
|
||||
private void ProcessConnection(TcpClient tcpClient) {
|
||||
byte[] buffer = new byte[4];
|
||||
NetworkStream stream = tcpClient.GetStream();
|
||||
stream.ReadTimeout = kSocketReadTimeoutMillis;
|
||||
tcpClient.ReceiveTimeout = kSocketReadTimeoutMillis;
|
||||
while (!shouldStop) {
|
||||
int bytesRead = blockingRead(stream, buffer, 0, 4);
|
||||
if (bytesRead < 4) {
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
"Failed to read from controller emulator app event socket." +
|
||||
"\nVerify that the controller emulator app is running.");
|
||||
}
|
||||
int msgLen = unpack32bits(correctEndianness(buffer), 0);
|
||||
|
||||
byte[] dataBuffer = new byte[msgLen];
|
||||
bytesRead = blockingRead(stream, dataBuffer, 0, msgLen);
|
||||
if (bytesRead < msgLen) {
|
||||
// Caught by phoneEventSocketLoop.
|
||||
throw new Exception(
|
||||
"Failed to read from controller emulator app event socket." +
|
||||
"\nVerify that the controller emulator app is running.");
|
||||
}
|
||||
|
||||
PhoneEvent proto =
|
||||
PhoneEvent.CreateBuilder().MergeFrom(dataBuffer).Build();
|
||||
phoneRemote.OnPhoneEvent(proto);
|
||||
|
||||
connected = EmulatorClientSocketConnectionState.Connected;
|
||||
|
||||
if (!lastConnectionAttemptWasSuccessful) {
|
||||
Debug.Log("Successfully connected to controller emulator app.");
|
||||
// Log first failure after after successful read from event socket.
|
||||
lastConnectionAttemptWasSuccessful = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int blockingRead(NetworkStream stream, byte[] buffer, int index,
|
||||
int count) {
|
||||
int bytesRead = 0;
|
||||
while (!shouldStop && bytesRead < count) {
|
||||
try {
|
||||
int n = stream.Read(buffer, index + bytesRead, count - bytesRead);
|
||||
if (n <= 0) {
|
||||
// Failed to read.
|
||||
return -1;
|
||||
}
|
||||
bytesRead += n;
|
||||
} catch (IOException) {
|
||||
// Read failed or timed out.
|
||||
return -1;
|
||||
} catch (ObjectDisposedException) {
|
||||
// Socket closed.
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
void OnDestroy() {
|
||||
shouldStop = true;
|
||||
|
||||
if (phoneMirroringSocket != null) {
|
||||
phoneMirroringSocket.Close ();
|
||||
phoneMirroringSocket = null;
|
||||
}
|
||||
|
||||
if (phoneEventThread != null) {
|
||||
phoneEventThread.Join();
|
||||
}
|
||||
}
|
||||
|
||||
private int unpack32bits(byte[] array, int offset) {
|
||||
int num = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
num += array [offset + i] << (i * 8);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static private byte[] correctEndianness(byte[] array) {
|
||||
if (BitConverter.IsLittleEndian)
|
||||
Array.Reverse(array);
|
||||
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bf1f92fb4ae24291b71e77c1ccac323
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
class EmulatorConfig : MonoBehaviour {
|
||||
public static EmulatorConfig Instance {
|
||||
get {
|
||||
if (instance == null) {
|
||||
EmulatorConfig[] configs = (EmulatorConfig[]) FindObjectsOfType(typeof(EmulatorConfig));
|
||||
if (configs.Length == 1) {
|
||||
instance = configs[0];
|
||||
} else if (configs.Length > 1) {
|
||||
Debug.LogError(
|
||||
"Multiple PhoneRemote/Config objects in scene. Ignoring all.");
|
||||
}
|
||||
}
|
||||
if (instance == null) {
|
||||
var gameObject = new GameObject("PhoneRemoteConfig");
|
||||
instance = gameObject.AddComponent<EmulatorConfig>();
|
||||
DontDestroyOnLoad(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
private static EmulatorConfig instance = null;
|
||||
|
||||
public enum Mode {
|
||||
OFF,
|
||||
USB,
|
||||
WIFI,
|
||||
}
|
||||
|
||||
// Set this value to match how the PC is connected to the phone that is
|
||||
// streaming gyro, accel, and touch events. Set to OFF if using Wifi instead.
|
||||
public Mode PHONE_EVENT_MODE = Mode.USB;
|
||||
|
||||
/*----- Internal Parameters (should not require any changes). -----*/
|
||||
|
||||
// IP address of the phone, when connected to the PC via USB.
|
||||
public static readonly string USB_SERVER_IP = "127.0.0.1";
|
||||
|
||||
// IP address of the phone, when connected to the PC via WiFi.
|
||||
public static readonly string WIFI_SERVER_IP = "192.168.43.1";
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a229fefd8ee7448b0b700f6000ebdec3
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,189 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
using proto;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
struct EmulatorGyroEvent {
|
||||
public readonly long timestamp;
|
||||
public readonly Vector3 value;
|
||||
|
||||
public EmulatorGyroEvent(PhoneEvent.Types.GyroscopeEvent proto) {
|
||||
timestamp = proto.Timestamp;
|
||||
value = new Vector3(proto.X, proto.Y, proto.Z);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorAccelEvent {
|
||||
public readonly long timestamp;
|
||||
public readonly Vector3 value;
|
||||
|
||||
public EmulatorAccelEvent(PhoneEvent.Types.AccelerometerEvent proto) {
|
||||
timestamp = proto.Timestamp;
|
||||
value = new Vector3(proto.X, proto.Y, proto.Z);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorTouchEvent {
|
||||
// Action constants. These should match the constants in the Android
|
||||
// MotionEvent:
|
||||
// http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_CANCEL
|
||||
public enum Action {
|
||||
kActionDown = 0,
|
||||
kActionUp = 1,
|
||||
kActionMove = 2,
|
||||
kActionCancel = 3,
|
||||
kActionPointerDown = 5,
|
||||
kActionPointerUp = 6,
|
||||
kActionHoverMove = 7,
|
||||
kActionHoverEnter = 9,
|
||||
kActionHoverExit = 10
|
||||
};
|
||||
|
||||
// Use getActionMasked() and getActionPointer() instead.
|
||||
private readonly int action;
|
||||
public readonly int relativeTimestamp;
|
||||
public readonly List<Pointer> pointers;
|
||||
|
||||
public struct Pointer {
|
||||
public readonly int fingerId;
|
||||
public readonly float normalizedX;
|
||||
public readonly float normalizedY;
|
||||
|
||||
public Pointer(int fingerId, float normalizedX, float normalizedY) {
|
||||
this.fingerId = fingerId;
|
||||
this.normalizedX = normalizedX;
|
||||
this.normalizedY = normalizedY;
|
||||
}
|
||||
|
||||
public override string ToString () {
|
||||
return string.Format ("({0}, {1}, {2})", fingerId, normalizedX,
|
||||
normalizedY);
|
||||
}
|
||||
}
|
||||
|
||||
public EmulatorTouchEvent(PhoneEvent.Types.MotionEvent proto, long lastDownTimeMs) {
|
||||
action = proto.Action;
|
||||
relativeTimestamp =
|
||||
(Action)(proto.Action & ACTION_MASK) == Action.kActionDown
|
||||
? 0 : (int) (proto.Timestamp - lastDownTimeMs);
|
||||
pointers = new List<Pointer>();
|
||||
foreach (PhoneEvent.Types.MotionEvent.Types.Pointer pointer in
|
||||
proto.PointersList) {
|
||||
pointers.Add(
|
||||
new Pointer(pointer.Id, pointer.NormalizedX, pointer.NormalizedY));
|
||||
}
|
||||
}
|
||||
|
||||
public EmulatorTouchEvent(Action action, int pointerId, int relativeTimestamp,
|
||||
List<Pointer> pointers) {
|
||||
int fingerIndex = 0;
|
||||
if (action == Action.kActionPointerDown
|
||||
|| action == Action.kActionPointerUp) {
|
||||
fingerIndex = findPointerIndex(pointerId, pointers);
|
||||
if (fingerIndex == -1) {
|
||||
Debug.LogWarning("Could not find specific fingerId " + pointerId
|
||||
+ " in the supplied list of pointers.");
|
||||
fingerIndex = 0;
|
||||
}
|
||||
}
|
||||
this.action = getActionUnmasked(action, fingerIndex);
|
||||
this.relativeTimestamp = relativeTimestamp;
|
||||
this.pointers = pointers;
|
||||
}
|
||||
|
||||
// See Android's getActionMasked() and getActionIndex().
|
||||
private static readonly int ACTION_POINTER_INDEX_SHIFT = 8;
|
||||
private static readonly int ACTION_POINTER_INDEX_MASK = 0xff00;
|
||||
private static readonly int ACTION_MASK = 0xff;
|
||||
|
||||
public Action getActionMasked() {
|
||||
return (Action)(action & ACTION_MASK);
|
||||
}
|
||||
|
||||
public Pointer getActionPointer() {
|
||||
int index =
|
||||
(action & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
|
||||
return pointers[index];
|
||||
}
|
||||
|
||||
|
||||
private static int getActionUnmasked(Action action, int fingerIndex) {
|
||||
return ((int)action) | (fingerIndex << ACTION_POINTER_INDEX_SHIFT);
|
||||
}
|
||||
|
||||
private static int findPointerIndex(int fingerId, List<Pointer> pointers) {
|
||||
// Encode the fingerId info into the action, as Android does. See Android's
|
||||
// getActionMasked() and getActionIndex().
|
||||
int fingerIndex = -1;
|
||||
for (int i = 0; i < pointers.Count; i++) {
|
||||
if (fingerId == pointers[i].fingerId) {
|
||||
fingerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fingerIndex;
|
||||
}
|
||||
|
||||
public override string ToString () {
|
||||
System.Text.StringBuilder builder = new System.Text.StringBuilder ();
|
||||
builder.AppendFormat("t = {0}; A = {1}; P = {2}; N = {3}; [",
|
||||
relativeTimestamp, getActionMasked (), getActionPointer ().fingerId,
|
||||
pointers.Count);
|
||||
for (int i = 0; i < pointers.Count; i++) {
|
||||
builder.Append(pointers[i]).Append (", ");
|
||||
}
|
||||
builder.Append ("]");
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorOrientationEvent {
|
||||
public readonly long timestamp;
|
||||
public readonly Quaternion orientation;
|
||||
|
||||
public EmulatorOrientationEvent(PhoneEvent.Types.OrientationEvent proto) {
|
||||
timestamp = proto.Timestamp;
|
||||
// Convert from right-handed coordinates to left-handed.
|
||||
orientation = new Quaternion(proto.X, proto.Y, -proto.Z, proto.W);
|
||||
}
|
||||
}
|
||||
|
||||
struct EmulatorButtonEvent {
|
||||
// Codes as reported by the IC app (reuses Android KeyEvent codes).
|
||||
public enum ButtonCode {
|
||||
kNone = 0,
|
||||
kHome = 3, // android.view.KeyEvent.KEYCODE_HOME
|
||||
kVolumeUp = 25, // android.view.KeyEvent.KEYCODE_VOLUME_UP
|
||||
kVolumeDown = 24, // android.view.KeyEvent.KEYCODE_VOLUME_DOWN
|
||||
kClick = 66, // android.view.KeyEvent.KEYCODE_ENTER
|
||||
kApp = 82, // android.view.KeyEvent.KEYCODE_MENU
|
||||
}
|
||||
|
||||
public readonly ButtonCode code;
|
||||
public readonly bool down;
|
||||
public EmulatorButtonEvent(PhoneEvent.Types.KeyEvent proto) {
|
||||
code = (ButtonCode) proto.Code;
|
||||
down = proto.Action == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76d2b695633884daf905c07095c8a01c
|
||||
timeCreated: 1462051657
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,250 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This class is only used in the Editor, so make sure to only compile it on that platform.
|
||||
// Additionally, it depends on EmulatorClientSocket which is only compiled in the editor.
|
||||
// This MonoBehaviour is only ever instantiated dynamically, so it is fine that it is only compiled in the Editor,
|
||||
// Otherwise it would cause serialization issues.
|
||||
#if UNITY_EDITOR
|
||||
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
using proto;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
class EmulatorManager : MonoBehaviour {
|
||||
|
||||
private IEnumerator emulatorUpdate;
|
||||
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
|
||||
|
||||
public static EmulatorManager Instance {
|
||||
get {
|
||||
if (instance == null) {
|
||||
var gameObject = new GameObject("PhoneRemote");
|
||||
instance = gameObject.AddComponent<EmulatorManager>();
|
||||
// This object should survive all scene transitions.
|
||||
GameObject.DontDestroyOnLoad(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
private static EmulatorManager instance = null;
|
||||
|
||||
public delegate void OnGyroEvent(EmulatorGyroEvent gyroEvent);
|
||||
public event OnGyroEvent gyroEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentGyroEvent);
|
||||
}
|
||||
gyroEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
gyroEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnAccelEvent(EmulatorAccelEvent accelEvent);
|
||||
public event OnAccelEvent accelEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentAccelEvent);
|
||||
}
|
||||
accelEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
accelEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnTouchEvent(EmulatorTouchEvent touchEvent);
|
||||
public event OnTouchEvent touchEventListeners {
|
||||
add {
|
||||
if (value != null
|
||||
&& currentTouchEvent.pointers != null /* null only during init */) {
|
||||
value(currentTouchEvent);
|
||||
}
|
||||
touchEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
touchEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnOrientationEvent(EmulatorOrientationEvent orientationEvent);
|
||||
public event OnOrientationEvent orientationEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentOrientationEvent);
|
||||
}
|
||||
orientationEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
orientationEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void OnButtonEvent(EmulatorButtonEvent buttonEvent);
|
||||
public event OnButtonEvent buttonEventListeners {
|
||||
add {
|
||||
if (value != null) {
|
||||
value(currentButtonEvent);
|
||||
}
|
||||
buttonEventListenersInternal += value;
|
||||
}
|
||||
|
||||
remove {
|
||||
buttonEventListenersInternal -= value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onGyroEvent(EmulatorGyroEvent e) {
|
||||
currentGyroEvent = e;
|
||||
if (gyroEventListenersInternal != null) {
|
||||
gyroEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onAccelEvent(EmulatorAccelEvent e) {
|
||||
currentAccelEvent = e;
|
||||
if (accelEventListenersInternal != null) {
|
||||
accelEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onTouchEvent(EmulatorTouchEvent e) {
|
||||
currentTouchEvent = e;
|
||||
if (touchEventListenersInternal != null) {
|
||||
touchEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onOrientationEvent(EmulatorOrientationEvent e) {
|
||||
currentOrientationEvent = e;
|
||||
if (orientationEventListenersInternal != null) {
|
||||
orientationEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onButtonEvent(EmulatorButtonEvent e) {
|
||||
currentButtonEvent = e;
|
||||
if (buttonEventListenersInternal != null) {
|
||||
buttonEventListenersInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
EmulatorGyroEvent currentGyroEvent;
|
||||
EmulatorAccelEvent currentAccelEvent;
|
||||
EmulatorTouchEvent currentTouchEvent;
|
||||
EmulatorOrientationEvent currentOrientationEvent;
|
||||
EmulatorButtonEvent currentButtonEvent;
|
||||
|
||||
private event OnGyroEvent gyroEventListenersInternal;
|
||||
private event OnAccelEvent accelEventListenersInternal;
|
||||
private event OnTouchEvent touchEventListenersInternal;
|
||||
private event OnOrientationEvent orientationEventListenersInternal;
|
||||
private event OnButtonEvent buttonEventListenersInternal;
|
||||
|
||||
private Queue pendingEvents = Queue.Synchronized(new Queue());
|
||||
private EmulatorClientSocket socket;
|
||||
private long lastDownTimeMs;
|
||||
|
||||
public bool Connected {
|
||||
get {
|
||||
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Connecting {
|
||||
get {
|
||||
return socket != null && socket.connected == EmulatorClientSocketConnectionState.Connecting;
|
||||
}
|
||||
}
|
||||
|
||||
public void Awake() {
|
||||
if (instance == null) {
|
||||
instance = this;
|
||||
}
|
||||
if (instance != this) {
|
||||
Debug.LogWarning("PhoneRemote must be a singleton.");
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void Start() {
|
||||
socket = gameObject.AddComponent<EmulatorClientSocket>();
|
||||
socket.Init(this);
|
||||
emulatorUpdate = EndOfFrame();
|
||||
StartCoroutine(emulatorUpdate);
|
||||
}
|
||||
|
||||
IEnumerator EndOfFrame() {
|
||||
while (true) {
|
||||
yield return waitForEndOfFrame;
|
||||
lock (pendingEvents.SyncRoot) {
|
||||
while (pendingEvents.Count > 0) {
|
||||
PhoneEvent phoneEvent = (PhoneEvent) pendingEvents.Dequeue();
|
||||
ProcessEventAtEndOfFrame(phoneEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPhoneEvent(PhoneEvent e) {
|
||||
pendingEvents.Enqueue(e);
|
||||
}
|
||||
|
||||
private void ProcessEventAtEndOfFrame(PhoneEvent e) {
|
||||
switch (e.Type) {
|
||||
case PhoneEvent.Types.Type.MOTION:
|
||||
EmulatorTouchEvent touchEvent = new EmulatorTouchEvent(e.MotionEvent, lastDownTimeMs);
|
||||
onTouchEvent(touchEvent);
|
||||
if (touchEvent.getActionMasked() == EmulatorTouchEvent.Action.kActionDown) {
|
||||
lastDownTimeMs = e.MotionEvent.Timestamp;
|
||||
}
|
||||
break;
|
||||
case PhoneEvent.Types.Type.GYROSCOPE:
|
||||
EmulatorGyroEvent gyroEvent = new EmulatorGyroEvent(e.GyroscopeEvent);
|
||||
onGyroEvent(gyroEvent);
|
||||
break;
|
||||
case PhoneEvent.Types.Type.ACCELEROMETER:
|
||||
EmulatorAccelEvent accelEvent = new EmulatorAccelEvent(e.AccelerometerEvent);
|
||||
onAccelEvent(accelEvent);
|
||||
break;
|
||||
case PhoneEvent.Types.Type.ORIENTATION:
|
||||
EmulatorOrientationEvent orientationEvent =
|
||||
new EmulatorOrientationEvent(e.OrientationEvent);
|
||||
onOrientationEvent(orientationEvent);
|
||||
break;
|
||||
case PhoneEvent.Types.Type.KEY:
|
||||
EmulatorButtonEvent buttonEvent = new EmulatorButtonEvent(e.KeyEvent);
|
||||
onButtonEvent(buttonEvent);
|
||||
break;
|
||||
default:
|
||||
Debug.Log("Unsupported PhoneEvent type: " + e.Type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#endif // UNITY_EDITOR
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c42ca6bb02b364893b127c681c158442
|
||||
timeCreated: 1462051658
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3506
Assets/GoogleVR/Scripts/Controller/Internal/Emulator/PhoneEvent.cs
Normal file
3506
Assets/GoogleVR/Scripts/Controller/Internal/Emulator/PhoneEvent.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a6b456eb0cd540a489e0f82c377b187
|
||||
timeCreated: 1462046540
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
|
||||
/// @cond
|
||||
namespace Gvr.Internal {
|
||||
/// Internal interface that abstracts an implementation of a controller.
|
||||
///
|
||||
/// Each platform has a different concrete implementation of a Controller Provider.
|
||||
/// For example, if running on the Unity Editor, we use an implementation that
|
||||
/// communicates with the controller emulator via USB or WiFi. If running on a real
|
||||
/// Android device, we use an implementation that uses the underlying Daydream controller API.
|
||||
interface IControllerProvider : IDisposable {
|
||||
/// True if controller has battery status support.
|
||||
bool SupportsBatteryStatus { get; }
|
||||
|
||||
/// Reads the number of controllers the system is configured to use. This does not
|
||||
/// indicate the number of currently connected controllers.
|
||||
int MaxControllerCount { get; }
|
||||
|
||||
/// Notifies the controller provider that the application has paused.
|
||||
void OnPause();
|
||||
|
||||
/// Notifies the controller provider that the application has resumed.
|
||||
void OnResume();
|
||||
|
||||
/// Reads the controller's current state and stores it in outState.
|
||||
void ReadState(ControllerState outState, int controller_id);
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a546592901a23411d99a5fef0ada01e7
|
||||
timeCreated: 1462043673
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GoogleVR/Scripts/Controller/Tooltips.meta
Normal file
8
Assets/GoogleVR/Scripts/Controller/Tooltips.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee9f54f8732db114da4ba6cefc260101
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The controller is not available for versions of Unity without the
|
||||
// GVR native integration.
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
/// A lightweight tooltip designed to minimize draw calls.
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrControllerTooltipsSimple")]
|
||||
public class GvrControllerTooltipsSimple : MonoBehaviour, IGvrArmModelReceiver {
|
||||
|
||||
private MeshRenderer tooltipRenderer;
|
||||
|
||||
public GvrBaseArmModel ArmModel { get; set; }
|
||||
|
||||
private MaterialPropertyBlock materialPropertyBlock;
|
||||
private int colorId;
|
||||
|
||||
void Awake() {
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void OnEnable() {
|
||||
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
|
||||
}
|
||||
|
||||
void OnDisable() {
|
||||
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
|
||||
}
|
||||
|
||||
void OnValidate() {
|
||||
if (!Application.isPlaying){
|
||||
Initialize();
|
||||
OnVisualUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize(){
|
||||
if(tooltipRenderer == null){
|
||||
tooltipRenderer = GetComponent<MeshRenderer>();
|
||||
}
|
||||
if(materialPropertyBlock == null){
|
||||
materialPropertyBlock = new MaterialPropertyBlock();
|
||||
}
|
||||
colorId = Shader.PropertyToID("_Color");
|
||||
}
|
||||
|
||||
private void OnPostControllerInputUpdated() {
|
||||
OnVisualUpdate();
|
||||
}
|
||||
|
||||
protected void OnVisualUpdate () {
|
||||
float alpha = ArmModel != null ? ArmModel.TooltipAlphaValue : 1.0f;
|
||||
materialPropertyBlock.SetColor(colorId, new Color(1,1,1,alpha));
|
||||
tooltipRenderer.SetPropertyBlock(materialPropertyBlock);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef63f87a26b474e1c88f77dfc4f3aa3a
|
||||
timeCreated: 1497972948
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
211
Assets/GoogleVR/Scripts/Controller/Tooltips/GvrTooltip.cs
Normal file
211
Assets/GoogleVR/Scripts/Controller/Tooltips/GvrTooltip.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections;
|
||||
|
||||
/// A tooltip for displaying control schemes overlaying the controller visual using a Unity Canvas.
|
||||
/// Automatically changes what side of the controller the tooltip is shown on depending
|
||||
/// on the handedness setting for the player.
|
||||
/// Automatically fades out when the controller visual is too close or too far
|
||||
/// away from the player's head.
|
||||
/// Look at the prefab GvrControllerPointer to see an example of how to use this script.
|
||||
[RequireComponent(typeof(CanvasGroup))]
|
||||
[RequireComponent(typeof(RectTransform))]
|
||||
[ExecuteInEditMode]
|
||||
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrTooltip")]
|
||||
public class GvrTooltip : MonoBehaviour, IGvrArmModelReceiver {
|
||||
/// Rotation for a tooltip when it is displayed on the right side of the controller visual.
|
||||
protected static readonly Quaternion RIGHT_SIDE_ROTATION = Quaternion.Euler(0.0f, 0.0f, 0.0f);
|
||||
|
||||
/// Rotation for a tooltip when it is displayed on the left side of the controller visual.
|
||||
protected static readonly Quaternion LEFT_SIDE_ROTATION = Quaternion.Euler(0.0f, 0.0f, 180.0f);
|
||||
|
||||
/// Anchor point for a tooltip, used for controlling what side the tooltip is on.
|
||||
protected static readonly Vector2 SQUARE_CENTER = new Vector2(0.5f, 0.5f);
|
||||
|
||||
/// Pivot point for a tooltip, used for controlling what side the tooltip is on.
|
||||
protected static readonly Vector2 PIVOT = new Vector2(-0.5f, 0.5f);
|
||||
|
||||
/// Y Position for touch pad tooltips based on the standard controller visual.
|
||||
protected const float TOUCH_PAD_Y_POSITION_METERS = 0.0385f;
|
||||
|
||||
/// Y position for app button tooltips based on the standard controller visual.
|
||||
protected const float APP_BUTTON_Y_POSITION_METERS = 0.0105f;
|
||||
|
||||
/// Z position for all tooltips based on the standard controller visual.
|
||||
protected const float TOOLTIP_Z_POSITION_METERS = 0.0098f;
|
||||
|
||||
/// Options for where the controller should be displayed.
|
||||
/// If set to custom, then the manually set localPosition of the tooltip is used.
|
||||
/// This is useful when displaying a tooltip for a non-standard controller visual.
|
||||
enum Location {
|
||||
TouchPadOutside,
|
||||
TouchPadInside,
|
||||
AppButtonOutside,
|
||||
AppButtonInside,
|
||||
Custom
|
||||
};
|
||||
|
||||
[Tooltip("The location to display the tooltip at relative to the controller visual.")]
|
||||
[SerializeField]
|
||||
private Location location;
|
||||
|
||||
[Tooltip("The text field for this tooltip.")]
|
||||
[SerializeField]
|
||||
private Text text;
|
||||
|
||||
[Tooltip("Determines if the tooltip is always visible regardless of the controller's location.")]
|
||||
[SerializeField]
|
||||
private bool alwaysVisible;
|
||||
|
||||
private bool isOnLeft = false;
|
||||
private RectTransform rectTransform;
|
||||
private CanvasGroup canvasGroup;
|
||||
|
||||
/// The text field for this tooltip.
|
||||
public Text TooltipText {
|
||||
get {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
public GvrBaseArmModel ArmModel { get; set; }
|
||||
|
||||
void Awake() {
|
||||
rectTransform = GetComponent<RectTransform>();
|
||||
canvasGroup = GetComponent<CanvasGroup>();
|
||||
isOnLeft = IsTooltipOnLeft();
|
||||
RefreshTooltip();
|
||||
}
|
||||
|
||||
void OnEnable() {
|
||||
// Update using OnPostControllerInputUpdated.
|
||||
// This way, the position and rotation will be correct for the entire frame
|
||||
// so that it doesn't matter what order Updates get called in.
|
||||
if (Application.isPlaying) {
|
||||
GvrControllerInput.OnPostControllerInputUpdated += OnPostControllerInputUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable() {
|
||||
GvrControllerInput.OnPostControllerInputUpdated -= OnPostControllerInputUpdated;
|
||||
}
|
||||
|
||||
private void OnPostControllerInputUpdated() {
|
||||
CheckTooltipSide();
|
||||
|
||||
if (canvasGroup != null && ArmModel != null) {
|
||||
canvasGroup.alpha = alwaysVisible ? 1.0f : ArmModel.TooltipAlphaValue;
|
||||
}
|
||||
}
|
||||
|
||||
void OnValidate() {
|
||||
rectTransform = GetComponent<RectTransform>();
|
||||
RefreshTooltip();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnRenderObject() {
|
||||
if (!Application.isPlaying) {
|
||||
CheckTooltipSide();
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
|
||||
/// Returns true if this tooltip is set to display on the inside of the controller.
|
||||
public bool IsTooltipInside() {
|
||||
switch (location) {
|
||||
case Location.TouchPadInside:
|
||||
case Location.AppButtonInside:
|
||||
case Location.Custom:
|
||||
return true;
|
||||
case Location.TouchPadOutside:
|
||||
case Location.AppButtonOutside:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the tooltip should display on the left side of the controller.
|
||||
/// This will change based on the handedness of the controller, as well as if the
|
||||
/// tooltip is set to display inside or outside.
|
||||
public bool IsTooltipOnLeft() {
|
||||
bool isInside = IsTooltipInside();
|
||||
GvrSettings.UserPrefsHandedness handedness = GvrSettings.Handedness;
|
||||
|
||||
if (handedness == GvrSettings.UserPrefsHandedness.Left) {
|
||||
return !isInside;
|
||||
} else {
|
||||
return isInside;
|
||||
}
|
||||
}
|
||||
|
||||
/// Refreshes how the tooltip is being displayed based on what side it is being shown on.
|
||||
/// Override to add custom display functionality.
|
||||
protected virtual void OnSideChanged(bool IsLocationOnLeft) {
|
||||
transform.localRotation = (isOnLeft ? LEFT_SIDE_ROTATION : RIGHT_SIDE_ROTATION);
|
||||
|
||||
if (text != null) {
|
||||
text.transform.localRotation = (IsLocationOnLeft ? LEFT_SIDE_ROTATION : RIGHT_SIDE_ROTATION);
|
||||
text.alignment = (IsLocationOnLeft ? TextAnchor.MiddleRight : TextAnchor.MiddleLeft);
|
||||
}
|
||||
}
|
||||
|
||||
protected float GetMetersToCanvasScale() {
|
||||
return GvrUIHelpers.GetMetersToCanvasScale(transform);
|
||||
}
|
||||
|
||||
private Vector3 GetLocalPosition() {
|
||||
float metersToCanvasScale = GetMetersToCanvasScale();
|
||||
|
||||
// Return early if we didn't find a valid metersToCanvasScale.
|
||||
if (metersToCanvasScale == 0.0f) {
|
||||
return rectTransform.anchoredPosition3D;
|
||||
}
|
||||
|
||||
float tooltipZPosition = TOOLTIP_Z_POSITION_METERS / metersToCanvasScale;
|
||||
switch (location) {
|
||||
case Location.TouchPadOutside:
|
||||
case Location.TouchPadInside:
|
||||
float touchPadYPosition = TOUCH_PAD_Y_POSITION_METERS / metersToCanvasScale;
|
||||
return new Vector3(0.0f, touchPadYPosition, tooltipZPosition);
|
||||
case Location.AppButtonOutside:
|
||||
case Location.AppButtonInside:
|
||||
float appButtonYPosition = APP_BUTTON_Y_POSITION_METERS / metersToCanvasScale;
|
||||
return new Vector3(0.0f, appButtonYPosition, tooltipZPosition);
|
||||
case Location.Custom:
|
||||
default:
|
||||
return rectTransform.anchoredPosition3D;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckTooltipSide() {
|
||||
// If handedness changes, the tooltip will switch sides.
|
||||
bool newIsOnLeft = IsTooltipOnLeft();
|
||||
if (newIsOnLeft != isOnLeft) {
|
||||
isOnLeft = newIsOnLeft;
|
||||
RefreshTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshTooltip() {
|
||||
rectTransform.anchorMax = SQUARE_CENTER;
|
||||
rectTransform.anchorMax = SQUARE_CENTER;
|
||||
rectTransform.pivot = PIVOT;
|
||||
rectTransform.anchoredPosition3D = GetLocalPosition();
|
||||
OnSideChanged(isOnLeft);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c52851ea0de74a228fa29a84de008ba
|
||||
timeCreated: 1481935272
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GoogleVR/Scripts/EventSystem.meta
Normal file
8
Assets/GoogleVR/Scripts/EventSystem.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbc29158ba66143438781e6b7ecf6d26
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
514
Assets/GoogleVR/Scripts/EventSystem/GvrBasePointer.cs
Normal file
514
Assets/GoogleVR/Scripts/EventSystem/GvrBasePointer.cs
Normal 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
|
||||
}
|
||||
12
Assets/GoogleVR/Scripts/EventSystem/GvrBasePointer.cs.meta
Normal file
12
Assets/GoogleVR/Scripts/EventSystem/GvrBasePointer.cs.meta
Normal 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:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b05767d6e8b854cd0987b344898cc15d
|
||||
timeCreated: 1478543740
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7441953e05443d4fa517d1ce7382b0c
|
||||
timeCreated: 1475082809
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a443597ee157fe49a30c4310f1fb2eb
|
||||
timeCreated: 1475082615
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 739800cd36aba44e9b04ce977e5784bd
|
||||
timeCreated: 1478217778
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb5dd43baba2d4dc1bab789615567e3d
|
||||
timeCreated: 1478196044
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
283
Assets/GoogleVR/Scripts/EventSystem/GvrPointerScrollInput.cs
Normal file
283
Assets/GoogleVR/Scripts/EventSystem/GvrPointerScrollInput.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23744ffc3b678488e858089d1a2973d9
|
||||
timeCreated: 1487096177
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/GoogleVR/Scripts/EventSystem/GvrScrollSettings.cs
Normal file
46
Assets/GoogleVR/Scripts/EventSystem/GvrScrollSettings.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5db1ab7348db34ecbac8c834c5d3425f
|
||||
timeCreated: 1496793989
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
57
Assets/GoogleVR/Scripts/EventSystem/GvrXREventsSubscriber.cs
Normal file
57
Assets/GoogleVR/Scripts/EventSystem/GvrXREventsSubscriber.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 12a8088f6fe474f31bffbd957940f5fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
40
Assets/GoogleVR/Scripts/EventSystem/IGvrScrollSettings.cs
Normal file
40
Assets/GoogleVR/Scripts/EventSystem/IGvrScrollSettings.cs
Normal 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; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4a605c315b944e7f84ed1dbe9ff6921
|
||||
timeCreated: 1496856692
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/GoogleVR/Scripts/EventSystem/InputModule.meta
Normal file
8
Assets/GoogleVR/Scripts/EventSystem/InputModule.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd315516f5723a340ad63cc669faf852
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca0157c63fb794df89c6735fc602eca2
|
||||
timeCreated: 1493228030
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a93ebe04d36b49c388adce4ac442226
|
||||
timeCreated: 1493142648
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fcd4baceb58cc40c98e500572bede6a6
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2e5fe232b3b94de2a12eb364ebc371e
|
||||
timeCreated: 1492661146
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 171f0a30d9ead4516a8fa319cfebe270
|
||||
timeCreated: 1493142648
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 118ed627eb676472d803284d1a988bbd
|
||||
timeCreated: 1492665020
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
64
Assets/GoogleVR/Scripts/GvrCardboardHelpers.cs
Normal file
64
Assets/GoogleVR/Scripts/GvrCardboardHelpers.cs
Normal 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
Reference in New Issue
Block a user