// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using Unity.Profiling;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Microsoft.MixedReality.Toolkit.Teleport
{
///
/// The Mixed Reality Toolkit's implementation of the .
///
public class MixedRealityTeleportSystem : BaseCoreSystem, IMixedRealityTeleportSystem
{
///
/// Constructor.
///
/// The instance that loaded the service.
[System.Obsolete("This constructor is obsolete (registrar parameter is no longer required) and will be removed in a future version of the Microsoft Mixed Reality Toolkit.")]
public MixedRealityTeleportSystem(
IMixedRealityServiceRegistrar registrar) : base(registrar, null) // Teleport system does not use a profile
{
Registrar = registrar;
}
///
/// Constructor.
///
public MixedRealityTeleportSystem() : base(null) { } // Teleport system does not use a profile
private TeleportEventData teleportEventData;
private bool isTeleporting = false;
private bool isProcessingTeleportRequest = false;
private Vector3 targetPosition = Vector3.zero;
private Vector3 targetRotation = Vector3.zero;
///
/// Used to clean up event system when shutting down, if this system created one.
///
private GameObject eventSystemReference = null;
#region IMixedRealityService Implementation
///
public override string Name { get; protected set; } = "Mixed Reality Teleport System";
///
public override void Initialize()
{
base.Initialize();
InitializeInternal();
}
private void InitializeInternal()
{
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
{
var eventSystems = Object.FindObjectsOfType();
if (eventSystems.Length == 0)
{
if (!IsInputSystemEnabled)
{
eventSystemReference = new GameObject("Event System");
eventSystemReference.AddComponent();
}
else
{
Debug.Log("The input system didn't properly add an event system to your scene. Please make sure the input system's priority is set higher than the teleport system.");
}
}
else if (eventSystems.Length > 1)
{
Debug.Log("Too many event systems in the scene. The Teleport System requires only one.");
}
}
#endif // UNITY_EDITOR
teleportEventData = new TeleportEventData(EventSystem.current);
}
///
public override void Destroy()
{
base.Destroy();
if (eventSystemReference != null)
{
if (!Application.isPlaying)
{
Object.DestroyImmediate(eventSystemReference);
}
else
{
Object.Destroy(eventSystemReference);
}
}
}
#endregion IMixedRealityService Implementation
#region IEventSystemManager Implementation
private static readonly ProfilerMarker HandleEventPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.HandleEvent");
///
public override void HandleEvent(BaseEventData eventData, ExecuteEvents.EventFunction eventHandler)
{
using (HandleEventPerfMarker.Auto())
{
Debug.Assert(eventData != null);
var teleportData = ExecuteEvents.ValidateEventData(eventData);
Debug.Assert(teleportData != null);
Debug.Assert(!teleportData.used);
// Process all the event listeners
base.HandleEvent(teleportData, eventHandler);
}
}
///
/// Register a GameObject to listen to teleport events.
///
public override void Register(GameObject listener) => base.Register(listener);
///
/// Unregister a GameObject from listening to teleport events.
///
public override void Unregister(GameObject listener) => base.Unregister(listener);
#endregion IEventSystemManager Implementation
#region IMixedRealityTeleportSystem Implementation
///
/// Is an input system registered?
///
private bool IsInputSystemEnabled => CoreServices.InputSystem != null;
private float teleportDuration = 0.25f;
///
public float TeleportDuration
{
get => teleportDuration;
set
{
if (isProcessingTeleportRequest)
{
Debug.LogWarning("Couldn't change teleport duration. Teleport in progress.");
return;
}
teleportDuration = value;
}
}
private static readonly ExecuteEvents.EventFunction OnTeleportRequestHandler =
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
{
var casted = ExecuteEvents.ValidateEventData(eventData);
handler.OnTeleportRequest(casted);
};
private static readonly ProfilerMarker RaiseTeleportRequestPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportRequest");
///
public void RaiseTeleportRequest(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
{
using (RaiseTeleportRequestPerfMarker.Auto())
{
// initialize event
teleportEventData.Initialize(pointer, hotSpot);
// Pass handler
HandleEvent(teleportEventData, OnTeleportRequestHandler);
}
}
private static readonly ExecuteEvents.EventFunction OnTeleportStartedHandler =
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
{
var casted = ExecuteEvents.ValidateEventData(eventData);
handler.OnTeleportStarted(casted);
};
private static readonly ProfilerMarker RaiseTeleportStartedPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportStarted");
///
public void RaiseTeleportStarted(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
{
if (isTeleporting)
{
Debug.LogError("Teleportation already in progress");
return;
}
using (RaiseTeleportStartedPerfMarker.Auto())
{
isTeleporting = true;
// initialize event
teleportEventData.Initialize(pointer, hotSpot);
// Pass handler
HandleEvent(teleportEventData, OnTeleportStartedHandler);
ProcessTeleportationRequest(teleportEventData);
}
}
private static readonly ExecuteEvents.EventFunction OnTeleportCompletedHandler =
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
{
var casted = ExecuteEvents.ValidateEventData(eventData);
handler.OnTeleportCompleted(casted);
};
private static readonly ProfilerMarker RaiseTeleportCompletePerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportComplete");
///
/// Raise a teleportation completed event.
///
/// The pointer that raised the event.
/// The teleport target
private void RaiseTeleportComplete(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
{
if (!isTeleporting)
{
Debug.LogError("No Active Teleportation in progress.");
return;
}
using (RaiseTeleportCompletePerfMarker.Auto())
{
// initialize event
teleportEventData.Initialize(pointer, hotSpot);
// Pass handler
HandleEvent(teleportEventData, OnTeleportCompletedHandler);
isTeleporting = false;
}
}
private static readonly ExecuteEvents.EventFunction OnTeleportCanceledHandler =
delegate (IMixedRealityTeleportHandler handler, BaseEventData eventData)
{
var casted = ExecuteEvents.ValidateEventData(eventData);
handler.OnTeleportCanceled(casted);
};
private static readonly ProfilerMarker RaiseTeleportCanceledPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.RaiseTeleportHandled");
///
public void RaiseTeleportCanceled(IMixedRealityPointer pointer, IMixedRealityTeleportHotspot hotSpot)
{
using (RaiseTeleportCanceledPerfMarker.Auto())
{
// initialize event
teleportEventData.Initialize(pointer, hotSpot);
// Pass handler
HandleEvent(teleportEventData, OnTeleportCanceledHandler);
}
}
#endregion IMixedRealityTeleportSystem Implementation
private static readonly ProfilerMarker ProcessTeleportationRequestPerfMarker = new ProfilerMarker("[MRTK] MixedRealityTeleportSystem.ProcessTeleportationRequest");
private void ProcessTeleportationRequest(TeleportEventData eventData)
{
using (ProcessTeleportationRequestPerfMarker.Auto())
{
isProcessingTeleportRequest = true;
targetRotation = Vector3.zero;
if (eventData.Pointer is IMixedRealityTeleportPointer teleportPointer && !teleportPointer.IsNull())
{
targetRotation.y = teleportPointer.PointerOrientation;
}
targetPosition = eventData.Pointer.Result.Details.Point;
if (eventData.Hotspot != null)
{
targetPosition = eventData.Hotspot.Position;
if (eventData.Hotspot.OverrideOrientation)
{
targetRotation.y = eventData.Hotspot.TargetRotation;
}
}
float height = targetPosition.y;
targetPosition -= CameraCache.Main.transform.position - MixedRealityPlayspace.Position;
targetPosition.y = height;
MixedRealityPlayspace.Position = targetPosition;
MixedRealityPlayspace.RotateAround(
CameraCache.Main.transform.position,
Vector3.up,
targetRotation.y - CameraCache.Main.transform.eulerAngles.y);
isProcessingTeleportRequest = false;
// Raise complete event using the pointer and hot spot provided.
RaiseTeleportComplete(eventData.Pointer, eventData.Hotspot);
}
}
}
}