182 lines
6.0 KiB
C#
182 lines
6.0 KiB
C#
|
// 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
|
|||
|
}
|