Added VR libraries

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

View File

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

View File

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

View File

@@ -0,0 +1,66 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections.Generic;
/// This script provides shared functionality used by all Gvr raycasters.
public abstract class GvrBasePointerRaycaster : BaseRaycaster {
private GvrBasePointer.PointerRay lastRay;
protected GvrBasePointer.RaycastMode CurrentRaycastModeForHybrid { get; private set; }
protected GvrBasePointerRaycaster() {
}
public GvrBasePointer.PointerRay GetLastRay() {
return lastRay;
}
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList) {
GvrBasePointer pointer = GvrPointerInputModule.Pointer;
if (pointer == null || !pointer.IsAvailable) {
return;
}
if (pointer.raycastMode == GvrBasePointer.RaycastMode.Hybrid) {
RaycastHybrid(pointer, eventData, resultAppendList);
} else {
RaycastDefault(pointer, eventData, resultAppendList);
}
}
protected abstract bool PerformRaycast(GvrBasePointer.PointerRay pointerRay, float radius,
PointerEventData eventData, List<RaycastResult> resultAppendList);
private void RaycastHybrid(GvrBasePointer pointer, PointerEventData eventData, List<RaycastResult> resultAppendList) {
CurrentRaycastModeForHybrid = GvrBasePointer.RaycastMode.Direct;
lastRay = GvrBasePointer.CalculateHybridRay(pointer, CurrentRaycastModeForHybrid);
float radius = pointer.CurrentPointerRadius;
bool foundHit = PerformRaycast(lastRay, radius, eventData, resultAppendList);
if (!foundHit) {
CurrentRaycastModeForHybrid = GvrBasePointer.RaycastMode.Camera;
lastRay = GvrBasePointer.CalculateHybridRay(pointer, CurrentRaycastModeForHybrid);
PerformRaycast(lastRay, radius, eventData, resultAppendList);
}
}
private void RaycastDefault(GvrBasePointer pointer, PointerEventData eventData, List<RaycastResult> resultAppendList) {
lastRay = GvrBasePointer.CalculateRay(pointer, pointer.raycastMode);
float radius = pointer.CurrentPointerRadius;
PerformRaycast(lastRay, radius, eventData, resultAppendList);
}
}

View File

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

View File

@@ -0,0 +1,22 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine.EventSystems;
/// Interface to implement if you wish to receive OnGvrPointerHover callbacks.
public interface IGvrPointerHoverHandler : IEventSystemHandler {
/// Called when pointer is hovering over GameObject.
void OnGvrPointerHover(PointerEventData eventData);
}

View File

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

View File

@@ -0,0 +1,30 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
/// This script extends the standard Unity EventSystem events with Gvr specific events.
public static class GvrExecuteEventsExtension {
private static readonly ExecuteEvents.EventFunction<IGvrPointerHoverHandler> s_HoverHandler = Execute;
private static void Execute(IGvrPointerHoverHandler handler, BaseEventData eventData) {
handler.OnGvrPointerHover(ExecuteEvents.ValidateEventData<PointerEventData>(eventData));
}
public static ExecuteEvents.EventFunction<IGvrPointerHoverHandler> pointerHoverHandler {
get { return s_HoverHandler; }
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,46 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using System.Collections;
/// Used to override the global scroll settings in _GvrPointerScrollInput_
/// for the GameObject that this script is attached to.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrScrollSettings")]
public class GvrScrollSettings : MonoBehaviour, IGvrScrollSettings {
/// Override the Inertia property in _GvrPointerScrollInput_ for this object.
///
/// Inertia means that scroll events will continue for a while after the user stops
/// touching the touchpad. It gradually slows down according to the decelerationRate.
[Tooltip("Determines if movement inertia is enabled.")]
public bool inertiaOverride = true;
/// The deceleration rate is the speed reduction per second.
/// A value of 0.5 halves the speed each second. The default is 0.05.
/// The deceleration rate is only used when inertia is enabled.
[Tooltip("The rate at which movement slows down.")]
public float decelerationRateOverride = 0.05f;
public bool InertiaOverride {
get {
return inertiaOverride;
}
}
public float DecelerationRateOverride {
get {
return decelerationRateOverride;
}
}
}

View File

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

View File

@@ -0,0 +1,57 @@
// Copyright 2018 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using System.Collections;
#if UNITY_2017_2_OR_NEWER
using UnityEngine.XR;
#else
using XRDevice = UnityEngine.VR.VRDevice;
using XRSettings = UnityEngine.VR.VRSettings;
#endif // UNITY_2017_2_OR_NEWER
// Handler for subscribing XR Unity actions to GVR Actions.
public class GvrXREventsSubscriber : MonoBehaviour {
private static GvrXREventsSubscriber instance;
private string _loadedDeviceName;
public static string loadedDeviceName {
get {
return GetInstance()._loadedDeviceName;
}
set {
GetInstance()._loadedDeviceName = value;
}
}
private static void OnDeviceLoadAction(string newLoadedDeviceName) {
loadedDeviceName = newLoadedDeviceName;
}
void Awake() {
instance = this;
_loadedDeviceName = XRSettings.loadedDeviceName;
#if UNITY_2018_3_OR_NEWER
XRDevice.deviceLoaded += OnDeviceLoadAction;
#endif // UNITY_2018_3_OR_NEWER
}
private static GvrXREventsSubscriber GetInstance() {
if (instance == null) {
GameObject gvrXREventsSubscriber = new GameObject("GvrXREventsSubscriber");
gvrXREventsSubscriber.AddComponent<GvrXREventsSubscriber>();
}
return instance;
}
}

View File

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

View File

@@ -0,0 +1,40 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using System.Collections;
/// Interface to implement to override the global scroll settings
/// in _GvrPointerScrollInput_ for an object.
///
/// Must be implmented by a component. It will override the scroll settings for the
/// GameObject that the component is attached to.
///
/// Can use _GvrScrollSettings_ To override scroll settings for any existing UI type,
/// or a custom UI component can implement this directly to override the scroll settings
/// for the UI component's use case.
public interface IGvrScrollSettings {
/// Override the Inertia property in _GvrPointerScrollInput_ for this object.
///
/// Inertia means that scroll events will continue for a while after the user stops
/// touching the touchpad. It gradually slows down according to the decelerationRate.
bool InertiaOverride { get; }
/// Override the DecelerationRate property in _GvrPointerScrollInput_ for this object.
///
/// The deceleration rate is the speed reduction per second.
/// A value of 0.5 halves the speed each second.
/// The deceleration rate is only used when inertia is enabled.
float DecelerationRateOverride { get; }
}

View File

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

View File

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

View File

@@ -0,0 +1,115 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
/// Exposes events from _GvrEventExecutor_ that are fired by _GvrPointerInputModule_ to the editor.
/// Makes it possible to handle EventSystem events globally.
[HelpURL("https://developers.google.com/vr/unity/reference/class/GvrAllEventsTrigger")]
public class GvrAllEventsTrigger : MonoBehaviour {
[Serializable]
public class TriggerEvent : UnityEvent<GameObject, PointerEventData>
{}
public TriggerEvent OnPointerClick;
public TriggerEvent OnPointerDown;
public TriggerEvent OnPointerUp;
public TriggerEvent OnPointerEnter;
public TriggerEvent OnPointerExit;
public TriggerEvent OnScroll;
private bool listenersAdded;
void OnEnable() {
AddListeners();
}
void OnDisable() {
RemoveListeners();
}
void Start() {
// The eventExecutor may not be available during OnEnable when the script is first created.
AddListeners();
}
private void AddListeners() {
GvrEventExecutor eventExecutor = GvrPointerInputModule.FindEventExecutor();
if (eventExecutor == null) {
return;
}
if (listenersAdded) {
return;
}
eventExecutor.OnPointerClick += OnPointerClickHandler;
eventExecutor.OnPointerDown += OnPointerDownHandler;
eventExecutor.OnPointerUp += OnPointerUpHandler;
eventExecutor.OnPointerEnter += OnPointerEnterHandler;
eventExecutor.OnPointerExit += OnPointerExitHandler;
eventExecutor.OnScroll += OnScrollHandler;
listenersAdded = true;
}
private void RemoveListeners() {
GvrEventExecutor eventExecutor = GvrPointerInputModule.FindEventExecutor();
if (eventExecutor == null) {
return;
}
if (!listenersAdded) {
return;
}
eventExecutor.OnPointerClick -= OnPointerClickHandler;
eventExecutor.OnPointerDown -= OnPointerDownHandler;
eventExecutor.OnPointerUp -= OnPointerUpHandler;
eventExecutor.OnPointerEnter -= OnPointerEnterHandler;
eventExecutor.OnPointerExit -= OnPointerExitHandler;
eventExecutor.OnScroll -= OnScrollHandler;
listenersAdded = false;
}
private void OnPointerClickHandler(GameObject target, PointerEventData eventData) {
OnPointerClick.Invoke(target, eventData);
}
private void OnPointerDownHandler(GameObject target, PointerEventData eventData) {
OnPointerDown.Invoke(target, eventData);
}
private void OnPointerUpHandler(GameObject target, PointerEventData eventData) {
OnPointerUp.Invoke(target, eventData);
}
private void OnPointerEnterHandler(GameObject target, PointerEventData eventData) {
OnPointerEnter.Invoke(target, eventData);
}
private void OnPointerExitHandler(GameObject target, PointerEventData eventData) {
OnPointerExit.Invoke(target, eventData);
}
private void OnScrollHandler(GameObject target, PointerEventData eventData) {
OnScroll.Invoke(target, eventData);
}
}

View File

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

View File

@@ -0,0 +1,159 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// Wraps UnityEngine.EventSystems.ExecuteEvents.
/// Also, exposes event delegates to allow global handling of events.
public class GvrEventExecutor : IGvrEventExecutor {
public delegate void EventDelegate(GameObject target, PointerEventData eventData);
/// Fired when a Click occurs on any object.
public event EventDelegate OnPointerClick {
add {
AddEventDelegate<IPointerClickHandler>(value);
}
remove {
RemoveEventDelegate<IPointerClickHandler>(value);
}
}
// Fired when a Down event occurs on any object.
public event EventDelegate OnPointerDown {
add {
AddEventDelegate<IPointerDownHandler>(value);
}
remove {
RemoveEventDelegate<IPointerDownHandler>(value);
}
}
// Fired when an Up event occurs on any object.
public event EventDelegate OnPointerUp {
add {
AddEventDelegate<IPointerUpHandler>(value);
}
remove {
RemoveEventDelegate<IPointerUpHandler>(value);
}
}
// Fired when an Enter event occurs on any object.
public event EventDelegate OnPointerEnter {
add {
AddEventDelegate<IPointerEnterHandler>(value);
}
remove {
RemoveEventDelegate<IPointerEnterHandler>(value);
}
}
// Fired when an Exit event occurs on any object.
public event EventDelegate OnPointerExit {
add {
AddEventDelegate<IPointerExitHandler>(value);
}
remove {
RemoveEventDelegate<IPointerExitHandler>(value);
}
}
// Fired when a Scroll event occurs on any object.
public event EventDelegate OnScroll {
add {
AddEventDelegate<IScrollHandler>(value);
}
remove {
RemoveEventDelegate<IScrollHandler>(value);
}
}
/// Stores delegates for events.
private Dictionary<Type, EventDelegate> eventTable;
public GvrEventExecutor() {
eventTable = new Dictionary<Type, EventDelegate>();
}
public bool Execute<T>(GameObject target,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> functor)
where T : IEventSystemHandler {
bool result = ExecuteEvents.Execute<T>(target, eventData, functor);
CallEventDelegate<T>(target, eventData);
return result;
}
public GameObject ExecuteHierarchy<T>(GameObject root,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> callbackFunction)
where T : IEventSystemHandler {
GameObject result = ExecuteEvents.ExecuteHierarchy<T>(root, eventData, callbackFunction);
CallEventDelegate<T>(root, eventData);
return result;
}
public GameObject GetEventHandler<T>(GameObject root)
where T : IEventSystemHandler {
return ExecuteEvents.GetEventHandler<T>(root);
}
private void CallEventDelegate<T>(GameObject target, BaseEventData eventData)
where T : IEventSystemHandler {
Type type = typeof(T);
EventDelegate eventDelegate;
if (eventTable.TryGetValue(type, out eventDelegate)) {
PointerEventData pointerEventData = eventData as PointerEventData;
if (pointerEventData == null) {
Debug.LogError("Event data must be PointerEventData.");
return;
}
eventDelegate(target, pointerEventData);
}
}
private void AddEventDelegate<T>(EventDelegate eventDelegate) {
Type type = typeof(T);
EventDelegate existingDelegate;
if (eventTable.TryGetValue(type, out existingDelegate)) {
eventTable[type] = existingDelegate + eventDelegate;
} else {
eventTable[type] = eventDelegate;
}
}
private void RemoveEventDelegate<T>(EventDelegate eventDelegate) {
Type type = typeof(T);
EventDelegate existingDelegate;
if (!eventTable.TryGetValue(type, out existingDelegate)) {
return;
}
eventDelegate = existingDelegate - eventDelegate;
if (eventDelegate != null) {
eventTable[type] = eventDelegate;
} else {
eventTable.Remove(type);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,32 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using UnityEngine;
using UnityEngine.EventSystems;
/// Provides an interface for executing events for _IEventSystemHandler_.
public interface IGvrEventExecutor {
bool Execute<T>(GameObject target,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> functor)
where T : IEventSystemHandler;
GameObject ExecuteHierarchy<T>(GameObject root,
BaseEventData eventData,
ExecuteEvents.EventFunction<T> callbackFunction)
where T : IEventSystemHandler;
GameObject GetEventHandler<T>(GameObject root)
where T : IEventSystemHandler;
}

View File

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

View File

@@ -0,0 +1,30 @@
// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
/// Interface for manipulating an InputModule used by _GvrPointerInputModuleImpl_
public interface IGvrInputModuleController {
EventSystem eventSystem { get; }
List<RaycastResult> RaycastResultCache { get; }
bool ShouldActivate();
void Deactivate();
GameObject FindCommonRoot(GameObject g1, GameObject g2);
BaseEventData GetBaseEventData();
RaycastResult FindFirstRaycast(List<RaycastResult> candidates);
}

View File

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