You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
409 lines
15 KiB
409 lines
15 KiB
//========= Copyright 2016-2018, HTC Corporation. All rights reserved. =========== |
|
|
|
using HTC.UnityPlugin.Utility; |
|
using System; |
|
using System.Collections.Generic; |
|
using System.Collections.ObjectModel; |
|
using UnityEngine; |
|
using UnityEngine.EventSystems; |
|
|
|
namespace HTC.UnityPlugin.Pointer3D |
|
{ |
|
public enum RaycastMode |
|
{ |
|
DefaultRaycast, |
|
Projection, |
|
Projectile, |
|
} |
|
|
|
// Contains and handles Pointer3DEventDatas, derived class should implement their own Pointer3DEventData |
|
// and add into buttonEventDataList, Pointer3DInputModule then will send eventData to the raycast target. |
|
public class Pointer3DRaycaster : BaseFallbackCamRaycaster |
|
{ |
|
public const float MIN_SEGMENT_DISTANCE = 0.01f; |
|
|
|
private ReadOnlyCollection<Pointer3DEventData> buttonEventDataListReadOnly; |
|
private ReadOnlyCollection<RaycastResult> sortedRaycastResultsReadOnly; |
|
private ReadOnlyCollection<Vector3> breakPointsReadOnly; |
|
|
|
protected readonly List<Pointer3DEventData> buttonEventDataList = new List<Pointer3DEventData>(); |
|
protected readonly List<RaycastResult> sortedRaycastResults = new List<RaycastResult>(); |
|
protected readonly List<Vector3> breakPoints = new List<Vector3>(); |
|
|
|
public float dragThreshold = 0.02f; |
|
public float clickInterval = 0.3f; |
|
|
|
public bool showDebugRay = true; |
|
|
|
public Pointer3DEventData HoverEventData { get { return buttonEventDataList.Count > 0 ? buttonEventDataList[0] : null; } } |
|
|
|
public ReadOnlyCollection<Pointer3DEventData> ButtonEventDataList |
|
{ |
|
get { return buttonEventDataListReadOnly ?? (buttonEventDataListReadOnly = buttonEventDataList.AsReadOnly()); } |
|
} |
|
|
|
public ReadOnlyCollection<RaycastResult> SortedRaycastResults |
|
{ |
|
get { return sortedRaycastResultsReadOnly ?? (sortedRaycastResultsReadOnly = sortedRaycastResults.AsReadOnly()); } |
|
} |
|
|
|
public ReadOnlyCollection<Vector3> BreakPoints |
|
{ |
|
get { return breakPointsReadOnly ?? (breakPointsReadOnly = breakPoints.AsReadOnly()); } |
|
} |
|
|
|
public virtual Vector2 GetScrollDelta() |
|
{ |
|
return Vector2.zero; |
|
} |
|
#if UNITY_EDITOR |
|
protected override void OnValidate() |
|
{ |
|
base.OnValidate(); |
|
|
|
// auto create RaySegmentGenerator if raycast mode is set when using previous version |
|
// and reset to DefaultRaycast since raycastMode is obsoleted |
|
if (m_raycastMode != RaycastMode.DefaultRaycast) |
|
{ |
|
if (Application.isPlaying) |
|
{ |
|
SetLagacyRaycastMode(m_raycastMode); |
|
m_raycastMode = RaycastMode.DefaultRaycast; |
|
} |
|
else |
|
{ |
|
// force saving changes of raycastMode |
|
// use delayCall because sometimes scene is not loaded and can't be marked dirty at this time |
|
UnityEditor.EditorApplication.delayCall += new UnityEditor.EditorApplication.CallbackFunction(() => |
|
{ |
|
Debug.LogWarning("The RaycastMode." + m_raycastMode + " setting has been replaced by adding " + (m_raycastMode == RaycastMode.Projection ? "ProjectionGenerator" : "ProjectileGenerator") |
|
+ " component. Please save the scene to preserve this changes."); |
|
SetLagacyRaycastMode(m_raycastMode); |
|
m_raycastMode = RaycastMode.DefaultRaycast; |
|
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene); |
|
UnityEditor.EditorUtility.SetDirty(this); |
|
}); |
|
} |
|
} |
|
} |
|
#endif |
|
protected override void Start() |
|
{ |
|
base.Start(); |
|
|
|
if (m_raycastMode != RaycastMode.DefaultRaycast) |
|
{ |
|
SetLagacyRaycastMode(m_raycastMode); |
|
m_raycastMode = RaycastMode.DefaultRaycast; |
|
} |
|
} |
|
|
|
// override OnEnable & OnDisable on purpose so that this BaseRaycaster won't be registered into RaycasterManager |
|
protected override void OnEnable() |
|
{ |
|
//base.OnEnable(); |
|
Pointer3DInputModule.AddRaycaster(this); |
|
} |
|
|
|
protected override void OnDisable() |
|
{ |
|
//base.OnDisable(); |
|
Pointer3DInputModule.RemoveRaycaster(this); |
|
} |
|
|
|
public virtual void CleanUpRaycast() |
|
{ |
|
sortedRaycastResults.Clear(); |
|
breakPoints.Clear(); |
|
} |
|
|
|
// invoke by Pointer3DInputModule |
|
public virtual void Raycast() |
|
{ |
|
sortedRaycastResults.Clear(); |
|
breakPoints.Clear(); |
|
|
|
var zScale = transform.lossyScale.z; |
|
var amountDistance = (FarDistance - NearDistance) * zScale; |
|
var origin = transform.TransformPoint(0f, 0f, NearDistance); |
|
|
|
breakPoints.Add(origin); |
|
|
|
bool hasNext = true; |
|
Vector3 direction; |
|
float distance; |
|
Ray ray; |
|
RaycastResult firstHit = default(RaycastResult); |
|
|
|
var generator = CurrentSegmentGenerator(); |
|
if (ReferenceEquals(generator, null)) |
|
{ |
|
// process default raycast |
|
|
|
direction = transform.forward; |
|
distance = amountDistance; |
|
ray = new Ray(origin, direction); |
|
|
|
// move event camera in place |
|
eventCamera.farClipPlane = eventCamera.nearClipPlane + distance; |
|
eventCamera.transform.position = ray.origin - (ray.direction * eventCamera.nearClipPlane); |
|
eventCamera.transform.rotation = Quaternion.LookRotation(ray.direction, transform.up); |
|
|
|
ForeachRaycastMethods(ray, distance, sortedRaycastResults); |
|
|
|
firstHit = FirstRaycastResult(); |
|
breakPoints.Add(firstHit.isValid ? firstHit.worldPosition : ray.GetPoint(distance)); |
|
#if UNITY_EDITOR |
|
if (showDebugRay) |
|
{ |
|
Debug.DrawLine(breakPoints[0], breakPoints[1], firstHit.isValid ? Color.green : Color.red); |
|
} |
|
#endif |
|
} |
|
else |
|
{ |
|
generator.ResetSegments(); |
|
|
|
do |
|
{ |
|
hasNext = generator.NextSegment(out direction, out distance); |
|
|
|
if (distance < MIN_SEGMENT_DISTANCE) |
|
{ |
|
Debug.LogWarning("RaySegment.distance cannot smaller than " + MIN_SEGMENT_DISTANCE + "! distance=" + distance.ToString("0.000")); |
|
break; |
|
} |
|
|
|
distance *= zScale; |
|
|
|
if (distance < amountDistance) |
|
{ |
|
amountDistance -= distance; |
|
} |
|
else |
|
{ |
|
distance = amountDistance; |
|
amountDistance = 0f; |
|
} |
|
|
|
ray = new Ray(origin, direction); |
|
|
|
// move event camera in place |
|
eventCamera.farClipPlane = eventCamera.nearClipPlane + distance; |
|
eventCamera.transform.position = ray.origin - (ray.direction * eventCamera.nearClipPlane); |
|
eventCamera.transform.rotation = Quaternion.LookRotation(ray.direction, transform.up); |
|
|
|
ForeachRaycastMethods(ray, distance, sortedRaycastResults); |
|
|
|
firstHit = FirstRaycastResult(); |
|
// end loop if raycast hit |
|
if (firstHit.isValid) |
|
{ |
|
breakPoints.Add(firstHit.worldPosition); |
|
#if UNITY_EDITOR |
|
if (showDebugRay) |
|
{ |
|
Debug.DrawLine(breakPoints[breakPoints.Count - 2], breakPoints[breakPoints.Count - 1], Color.green); |
|
} |
|
#endif |
|
break; |
|
} |
|
// otherwise, shift to next iteration |
|
origin = ray.GetPoint(distance); |
|
breakPoints.Add(origin); |
|
#if UNITY_EDITOR |
|
if (showDebugRay) |
|
{ |
|
Debug.DrawLine(breakPoints[breakPoints.Count - 2], breakPoints[breakPoints.Count - 1], Color.red); |
|
} |
|
#endif |
|
} |
|
while (hasNext && amountDistance > 0f); |
|
} |
|
} |
|
// called by StandaloneInputModule, not supported |
|
public override void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList) { } |
|
|
|
public RaycastResult FirstRaycastResult() |
|
{ |
|
for (int i = 0, imax = sortedRaycastResults.Count; i < imax; ++i) |
|
{ |
|
if (!sortedRaycastResults[i].isValid) { continue; } |
|
return sortedRaycastResults[i]; |
|
} |
|
return default(RaycastResult); |
|
} |
|
|
|
#region managing segment generators |
|
private IndexedSet<IRaySegmentGenerator> generators = new IndexedSet<IRaySegmentGenerator>(); |
|
|
|
public void AddGenerator(IRaySegmentGenerator generator) |
|
{ |
|
generators.AddUnique(generator); |
|
} |
|
|
|
public void RemoveGenerator(IRaySegmentGenerator generator) |
|
{ |
|
generators.Remove(generator); |
|
} |
|
|
|
// returns first enabled generator in generators, doesn't gaurantee any orders |
|
public IRaySegmentGenerator CurrentSegmentGenerator() |
|
{ |
|
for (int i = 0, imax = generators.Count; i < imax; ++i) |
|
{ |
|
if (generators[i].enabled) { return generators[i]; } |
|
} |
|
return null; |
|
} |
|
|
|
// enable or create TGenerator, disable others |
|
public TGenerator ForceEnableSegmentGenerator<TGenerator>() where TGenerator : MonoBehaviour, IRaySegmentGenerator |
|
{ |
|
var gen = default(TGenerator); |
|
var genFound = false; |
|
|
|
var gens = ListPool<IRaySegmentGenerator>.Get(); |
|
GetComponents(gens); |
|
for (int i = 0, imax = gens.Count; i < imax; ++i) |
|
{ |
|
if (genFound || !(gens[i] is TGenerator)) |
|
{ |
|
gens[i].enabled = false; |
|
} |
|
else |
|
{ |
|
gens[i].enabled = true; |
|
gen = gens[i] as TGenerator; |
|
genFound = true; |
|
} |
|
} |
|
ListPool<IRaySegmentGenerator>.Release(gens); |
|
|
|
if (!genFound) |
|
{ |
|
gen = gameObject.AddComponent<TGenerator>(); |
|
} |
|
|
|
return gen; |
|
} |
|
|
|
public void ForceDisableSegmentGenerators() |
|
{ |
|
var gens = ListPool<IRaySegmentGenerator>.Get(); |
|
GetComponents(gens); |
|
for (int i = 0, imax = gens.Count; i < imax; ++i) |
|
{ |
|
gens[i].enabled = false; |
|
} |
|
ListPool<IRaySegmentGenerator>.Release(gens); |
|
} |
|
#endregion |
|
|
|
#region obsolete interfaces |
|
[HideInInspector] |
|
[SerializeField] |
|
private RaycastMode m_raycastMode = RaycastMode.DefaultRaycast; |
|
[HideInInspector] |
|
[SerializeField] |
|
private float m_velocity = 2f; |
|
[HideInInspector] |
|
[SerializeField] |
|
private Vector3 m_gravity = Vector3.down; |
|
|
|
[Obsolete("Please use component ProjectionGenerator / ProjectileGenerator")] |
|
public RaycastMode raycastMode |
|
{ |
|
get { return m_raycastMode; } |
|
set |
|
{ |
|
m_raycastMode = value; |
|
SetLagacyRaycastMode(value); |
|
} |
|
} |
|
|
|
[Obsolete("Please use ProjectionGenerator.velocity / ProjectileGenerator.velocity")] |
|
public float velocity |
|
{ |
|
get { return m_velocity; } |
|
set |
|
{ |
|
m_velocity = value; |
|
var gen = CurrentSegmentGenerator(); |
|
if (gen != null) |
|
{ |
|
if (gen is ProjectionGenerator) { ((ProjectionGenerator)gen).velocity = value; } |
|
else if (gen is ProjectileGenerator) { ((ProjectileGenerator)gen).velocity = value; } |
|
} |
|
} |
|
} |
|
|
|
[Obsolete("Please use ProjectionGenerator.gravity / ProjectileGenerator.gravity")] |
|
public Vector3 gravity |
|
{ |
|
get { return m_gravity; } |
|
set |
|
{ |
|
m_gravity = value; |
|
var gen = CurrentSegmentGenerator(); |
|
if (gen != null) |
|
{ |
|
if (gen is ProjectionGenerator) { ((ProjectionGenerator)gen).gravity = value; } |
|
else if (gen is ProjectileGenerator) { ((ProjectileGenerator)gen).gravity = value; } |
|
} |
|
} |
|
} |
|
|
|
[Obsolete("Please use component ProjectionGenerator / ProjectileGenerator")] |
|
protected virtual void InitSegmentGenerator() { } |
|
|
|
// for backward compatible |
|
private void SetLagacyRaycastMode(RaycastMode mode) |
|
{ |
|
switch (mode) |
|
{ |
|
case RaycastMode.Projection: |
|
{ |
|
var gen = ForceEnableSegmentGenerator<ProjectionGenerator>(); |
|
gen.velocity = m_velocity; |
|
gen.gravity = m_gravity; |
|
break; |
|
} |
|
case RaycastMode.Projectile: |
|
{ |
|
var gen = ForceEnableSegmentGenerator<ProjectileGenerator>(); |
|
gen.velocity = m_velocity; |
|
gen.gravity = m_gravity; |
|
break; |
|
} |
|
default: |
|
{ |
|
ForceDisableSegmentGenerators(); |
|
break; |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
public override string ToString() |
|
{ |
|
var str = string.Empty; |
|
str += "Raycaster path: " + Pointer3DInputModule.PrintGOPath(gameObject) + "(" + GetType().Name + ")\n"; |
|
str += "Raycaster transform: " + "pos" + transform.position.ToString("0.00") + " " + "rot" + transform.eulerAngles.ToString("0.0") + "\n"; |
|
|
|
for (int i = 0, imax = buttonEventDataList.Count; i < imax; ++i) |
|
{ |
|
var eventData = buttonEventDataList[i]; |
|
if (eventData == null) { continue; } |
|
|
|
if (eventData.eligibleForClick || (i == 0 && eventData.pointerEnter != null)) // is hover event? |
|
{ |
|
str += "<b>EventData: [" + i + "]</b>\n"; |
|
str += eventData.ToString(); |
|
} |
|
} |
|
|
|
return str; |
|
} |
|
} |
|
} |