diff --git a/Editor/Profiles/TeleportSystem.meta b/Editor/Profiles/TeleportSystem.meta new file mode 100644 index 000000000..3783d1e40 --- /dev/null +++ b/Editor/Profiles/TeleportSystem.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6313bef2b1209b44c8b858f5ad745a18 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Profiles/MixedRealityTeleportSystemProfileInspector.cs b/Editor/Profiles/TeleportSystem/MixedRealityTeleportSystemProfileInspector.cs similarity index 66% rename from Editor/Profiles/MixedRealityTeleportSystemProfileInspector.cs rename to Editor/Profiles/TeleportSystem/MixedRealityTeleportSystemProfileInspector.cs index 8c449907a..bd6ab754a 100644 --- a/Editor/Profiles/MixedRealityTeleportSystemProfileInspector.cs +++ b/Editor/Profiles/TeleportSystem/MixedRealityTeleportSystemProfileInspector.cs @@ -4,21 +4,18 @@ using UnityEditor; using XRTK.Definitions.TeleportSystem; using XRTK.Services; -using XRTK.Services.Teleportation; -namespace XRTK.Editor.Profiles +namespace XRTK.Editor.Profiles.TeleportSystem { [CustomEditor(typeof(MixedRealityTeleportSystemProfile))] public class MixedRealityTeleportSystemProfileInspector : MixedRealityServiceProfileInspector { - private SerializedProperty teleportMode; private SerializedProperty teleportProvider; protected override void OnEnable() { base.OnEnable(); - teleportMode = serializedObject.FindProperty(nameof(teleportMode)); teleportProvider = serializedObject.FindProperty(nameof(teleportProvider)); } @@ -29,17 +26,7 @@ public override void OnInspectorGUI() serializedObject.Update(); EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(teleportMode); - - var activeTeleportMode = (TeleportMode)teleportMode.intValue; - switch (activeTeleportMode) - { - case TeleportMode.Provider: - EditorGUI.indentLevel++; - EditorGUILayout.PropertyField(teleportProvider); - EditorGUI.indentLevel--; - break; - } + EditorGUILayout.PropertyField(teleportProvider); serializedObject.ApplyModifiedProperties(); diff --git a/Editor/Profiles/MixedRealityTeleportSystemProfileInspector.cs.meta b/Editor/Profiles/TeleportSystem/MixedRealityTeleportSystemProfileInspector.cs.meta similarity index 100% rename from Editor/Profiles/MixedRealityTeleportSystemProfileInspector.cs.meta rename to Editor/Profiles/TeleportSystem/MixedRealityTeleportSystemProfileInspector.cs.meta diff --git a/Editor/Profiles/TeleportSystem/MixedRealityTeleportValidationDataProviderProfileInspector.cs b/Editor/Profiles/TeleportSystem/MixedRealityTeleportValidationDataProviderProfileInspector.cs new file mode 100644 index 000000000..259dea9a1 --- /dev/null +++ b/Editor/Profiles/TeleportSystem/MixedRealityTeleportValidationDataProviderProfileInspector.cs @@ -0,0 +1,41 @@ +// Copyright (c) XRTK. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using UnityEditor; +using XRTK.Definitions.TeleportSystem; + +namespace XRTK.Editor.Profiles.TeleportSystem +{ + [CustomEditor(typeof(MixedRealityTeleportValidationDataProviderProfile))] + public class MixedRealityTeleportValidationDataProviderProfileInspector : BaseMixedRealityProfileInspector + { + private SerializedProperty validLayers; + private SerializedProperty invalidLayers; + private SerializedProperty upDirectionThreshold; + private SerializedProperty maxDistance; + + protected override void OnEnable() + { + base.OnEnable(); + + validLayers = serializedObject.FindProperty(nameof(validLayers)); + invalidLayers = serializedObject.FindProperty(nameof(invalidLayers)); + upDirectionThreshold = serializedObject.FindProperty(nameof(upDirectionThreshold)); + maxDistance = serializedObject.FindProperty(nameof(maxDistance)); + } + + public override void OnInspectorGUI() + { + RenderHeader("This profile defines the set of rules to validate against when deciding whether a teleport target location is valid or not."); + + serializedObject.Update(); + + EditorGUILayout.PropertyField(validLayers); + EditorGUILayout.PropertyField(invalidLayers); + EditorGUILayout.PropertyField(upDirectionThreshold); + EditorGUILayout.PropertyField(maxDistance); + + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Editor/Profiles/TeleportSystem/MixedRealityTeleportValidationDataProviderProfileInspector.cs.meta b/Editor/Profiles/TeleportSystem/MixedRealityTeleportValidationDataProviderProfileInspector.cs.meta new file mode 100644 index 000000000..4a5cad11f --- /dev/null +++ b/Editor/Profiles/TeleportSystem/MixedRealityTeleportValidationDataProviderProfileInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: bf3758cb017cb79489d20083b027c454 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: + - logo: {instanceID: 0} + executionOrder: 0 + icon: {fileID: 2800000, guid: 6e2e9d716bbb4d8382bd53f11996b90e, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Definitions/Physics/TeleportSurfaceResult.cs b/Runtime/Definitions/Physics/TeleportSurfaceResult.cs deleted file mode 100644 index b5c12bf66..000000000 --- a/Runtime/Definitions/Physics/TeleportSurfaceResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See LICENSE in the project root for license information. - -using System; - -namespace XRTK.Definitions.Physics -{ - [Serializable] - public enum TeleportSurfaceResult - { - None = 0, - Valid, - Invalid, - HotSpot, - } -} diff --git a/Runtime/Definitions/TeleportSystem/MixedRealityTeleportSystemProfile.cs b/Runtime/Definitions/TeleportSystem/MixedRealityTeleportSystemProfile.cs index ff92bd2bb..cefeb1fc1 100644 --- a/Runtime/Definitions/TeleportSystem/MixedRealityTeleportSystemProfile.cs +++ b/Runtime/Definitions/TeleportSystem/MixedRealityTeleportSystemProfile.cs @@ -16,19 +16,6 @@ namespace XRTK.Definitions.TeleportSystem [CreateAssetMenu(menuName = "Mixed Reality Toolkit/Teleport System Profile", fileName = "MixedRealityTeleportSystemProfile", order = (int)CreateProfileMenuItemIndices.Input)] public class MixedRealityTeleportSystemProfile : BaseMixedRealityServiceProfile { - [SerializeField] - [Tooltip("The teleportation mode to use.")] - private TeleportMode teleportMode = TeleportMode.Default; - - /// - /// The teleportation mode to use. - /// - public TeleportMode TeleportMode - { - get => teleportMode; - internal set => teleportMode = value; - } - [SerializeField] [Tooltip("The concrete teleport provider to use for teleportation.")] [Implements(typeof(IMixedRealityTeleportProvider), TypeGrouping.ByNamespaceFlat)] diff --git a/Runtime/Definitions/TeleportSystem/MixedRealityTeleportValidationDataProviderProfile.cs b/Runtime/Definitions/TeleportSystem/MixedRealityTeleportValidationDataProviderProfile.cs new file mode 100644 index 000000000..21b06bd68 --- /dev/null +++ b/Runtime/Definitions/TeleportSystem/MixedRealityTeleportValidationDataProviderProfile.cs @@ -0,0 +1,70 @@ +// Copyright (c) XRTK. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using UnityEngine; +using XRTK.Definitions.Utilities; +using XRTK.Services.Teleportation; + +namespace XRTK.Definitions.TeleportSystem +{ + /// + /// Configuration profile for the . + /// + [CreateAssetMenu(menuName = "Mixed Reality Toolkit/Teleport System/Teleport Validation Data Provider Profile", fileName = "MixedRealityTeleportValidationDataProviderProfile", order = (int)CreateProfileMenuItemIndices.Input)] + public class MixedRealityTeleportValidationDataProviderProfile : BaseMixedRealityProfile + { + [SerializeField] + [Tooltip("Layers that are considered 'valid' for teleportation.")] + private LayerMask validLayers = UnityEngine.Physics.DefaultRaycastLayers; + + /// + /// Layers that are considered 'valid' for teleportation. + /// + public LayerMask ValidLayers + { + get => validLayers; + internal set => validLayers = value; + } + + [SerializeField] + [Tooltip("Layers that are considered 'invalid' for teleportation.")] + private LayerMask invalidLayers = UnityEngine.Physics.IgnoreRaycastLayer; + + /// + /// Layers that are considered 'invalid' for teleportation. + /// + public LayerMask InvalidLayers + { + get => invalidLayers; + internal set => invalidLayers = value; + } + + [SerializeField] + [Range(0f, 1f)] + [Tooltip("The up direction threshold to use when determining if a surface is 'flat' enough to teleport to.")] + private float upDirectionThreshold = 0.2f; + + /// + /// The up direction threshold to use when determining if a surface is 'flat' enough to teleport to. + /// + public float UpDirectionThreshold + { + get => upDirectionThreshold; + internal set => upDirectionThreshold = value; + } + + [SerializeField] + [Min(.1f)] + [Tooltip("The maximum distance from the player a teleport location can be away.")] + private float maxDistance = 10f; + + /// + /// The maximum distance from the player a teleport location can be away. + /// + public float MaxDistance + { + get => maxDistance; + internal set => maxDistance = value; + } + } +} diff --git a/Runtime/Definitions/TeleportSystem/MixedRealityTeleportValidationDataProviderProfile.cs.meta b/Runtime/Definitions/TeleportSystem/MixedRealityTeleportValidationDataProviderProfile.cs.meta new file mode 100644 index 000000000..914f71899 --- /dev/null +++ b/Runtime/Definitions/TeleportSystem/MixedRealityTeleportValidationDataProviderProfile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6c3c644db71fa6140991a685a6b3dbb4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 6e2e9d716bbb4d8382bd53f11996b90e, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Interfaces/TeleportSystem/IMixedRealityTeleportValidationDataProvider.cs b/Runtime/Interfaces/TeleportSystem/IMixedRealityTeleportValidationDataProvider.cs new file mode 100644 index 000000000..2f4cb5246 --- /dev/null +++ b/Runtime/Interfaces/TeleportSystem/IMixedRealityTeleportValidationDataProvider.cs @@ -0,0 +1,24 @@ +// Copyright (c) XRTK. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using UnityEngine; +using XRTK.Interfaces.InputSystem; +using XRTK.Services.Teleportation; + +namespace XRTK.Interfaces.TeleportSystem +{ + /// + /// Interface to define teleportation validation data providers. + /// + public interface IMixedRealityTeleportValidationDataProvider : IMixedRealityTeleportDataProvider + { + /// + /// Validates a and returns whether the + /// qualifies for teleporation. + /// + /// The to validate. + /// found at the target position, if any. + /// The for . + TeleportValidationResult IsValid(IPointerResult pointerResult, IMixedRealityTeleportHotSpot teleportHotSpot = null); + } +} diff --git a/Runtime/Services/TeleportSystem/TeleportMode.cs.meta b/Runtime/Interfaces/TeleportSystem/IMixedRealityTeleportValidationDataProvider.cs.meta similarity index 86% rename from Runtime/Services/TeleportSystem/TeleportMode.cs.meta rename to Runtime/Interfaces/TeleportSystem/IMixedRealityTeleportValidationDataProvider.cs.meta index add585649..f7f3f0206 100644 --- a/Runtime/Services/TeleportSystem/TeleportMode.cs.meta +++ b/Runtime/Interfaces/TeleportSystem/IMixedRealityTeleportValidationDataProvider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e8d3a2f4a39c7024493058f95c033cda +guid: 5152bb956083202479d933ea47b9211b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Runtime/Services/MixedRealityToolkit.cs b/Runtime/Services/MixedRealityToolkit.cs index b0ccb20d2..76b72b3da 100644 --- a/Runtime/Services/MixedRealityToolkit.cs +++ b/Runtime/Services/MixedRealityToolkit.cs @@ -490,7 +490,12 @@ private void InitializeServiceLocator() if (ActiveProfile.IsTeleportSystemEnabled) { - if (!TryCreateAndRegisterService(ActiveProfile.TeleportSystemSystemType, out _, ActiveProfile.TeleportSystemProfile) || TeleportSystem == null) + if (TryCreateAndRegisterService(ActiveProfile.TeleportSystemSystemType, out var service, ActiveProfile.TeleportSystemProfile) || TeleportSystem == null) + { + TryRegisterDataProviderConfigurations(ActiveProfile.TeleportSystemProfile.RegisteredServiceConfigurations, service); + + } + else { Debug.LogError("Failed to start the Teleport System!"); } diff --git a/Runtime/Services/TeleportSystem/MixedRealityTeleportSystem.cs b/Runtime/Services/TeleportSystem/MixedRealityTeleportSystem.cs index 507925ef4..e0581791a 100644 --- a/Runtime/Services/TeleportSystem/MixedRealityTeleportSystem.cs +++ b/Runtime/Services/TeleportSystem/MixedRealityTeleportSystem.cs @@ -29,22 +29,10 @@ public class MixedRealityTeleportSystem : BaseEventSystem, IMixedRealityTeleport public MixedRealityTeleportSystem(MixedRealityTeleportSystemProfile profile) : base(profile) { - teleportMode = profile.TeleportMode; - if (teleportMode == TeleportMode.Provider) - { - if (profile.TeleportProvider?.Type == null) - { - throw new Exception($"The {nameof(MixedRealityTeleportSystemProfile)} is configured for " + - $"{TeleportMode.Provider} but is missing the required {teleportProvider}!"); - } - - teleportProvider = profile.TeleportProvider; - } + teleportProvider = profile.TeleportProvider?.Type == null ? null : teleportProvider = profile.TeleportProvider; } private readonly SystemType teleportProvider; - private readonly TeleportMode teleportMode; - private TeleportEventData teleportEventData; private bool isTeleporting = false; @@ -60,25 +48,27 @@ public override void Initialize() teleportEventData = new TeleportEventData(EventSystem.current); } - switch (teleportMode) + if (teleportProvider == null) { - case TeleportMode.Default: - var component = CameraCache.Main.GetComponent() as Component; - if (!component.IsNull()) + // No provier selected, we'll be using default teleport. + // Make sure to remove any leftover provider attached to the camera. + var component = CameraCache.Main.GetComponent() as Component; + if (!component.IsNull()) + { + if (Application.isPlaying) + { + Object.Destroy(component); + } + else { - if (Application.isPlaying) - { - Object.Destroy(component); - } - else - { - Object.DestroyImmediate(component); - } + Object.DestroyImmediate(component); } - break; - case TeleportMode.Provider: - CameraCache.Main.gameObject.EnsureComponent(teleportProvider.Type); - break; + } + } + else + { + // A provider is set, make sure it's attached to the camera. + CameraCache.Main.gameObject.EnsureComponent(teleportProvider.Type); } } @@ -178,7 +168,7 @@ public void RaiseTeleportStarted(IMixedRealityPointer pointer, IMixedRealityTele // In default teleportation mode we do not expect any provider // to handle teleportation, instead we simply perform an instant teleport. - if (teleportMode == TeleportMode.Default) + if (teleportProvider == null) { PerformDefaultTeleport(teleportEventData); } @@ -237,7 +227,7 @@ private void PerformDefaultTeleport(TeleportEventData eventData) CameraCache.Main.transform; var teleportTransform = cameraTransform.parent; Debug.Assert(teleportTransform != null, - $"{nameof(TeleportMode.Default)} requires that the camera be parented under another object!"); + $"{nameof(MixedRealityTeleportSystem)} without a provider set requires that the camera be parented under another object! Assign a teleport provider in the system profile or fix the camera setup."); var targetRotation = Vector3.zero; var targetPosition = eventData.Pointer.Result.EndPoint; diff --git a/Runtime/Services/TeleportSystem/MixedRealityTeleportValidationDataProvider.cs b/Runtime/Services/TeleportSystem/MixedRealityTeleportValidationDataProvider.cs new file mode 100644 index 000000000..f2701bad0 --- /dev/null +++ b/Runtime/Services/TeleportSystem/MixedRealityTeleportValidationDataProvider.cs @@ -0,0 +1,72 @@ +// Copyright (c) XRTK. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using UnityEngine; +using XRTK.Definitions.TeleportSystem; +using XRTK.Interfaces.InputSystem; +using XRTK.Interfaces.TeleportSystem; +using XRTK.Utilities; + +namespace XRTK.Services.Teleportation +{ + /// + /// The Mixed Reality Toolkit's specific implementation of the . + /// + [System.Runtime.InteropServices.Guid("14199fd8-1636-4147-bb08-6475e76ed1cd")] + public class MixedRealityTeleportValidationDataProvider : BaseDataProvider, IMixedRealityTeleportValidationDataProvider + { + /// + public MixedRealityTeleportValidationDataProvider(string name, uint priority, MixedRealityTeleportValidationDataProviderProfile profile, IMixedRealityTeleportSystem parentService) + : base(name, priority, profile, parentService) + { + validLayers = profile.ValidLayers; + invalidLayers = profile.InvalidLayers; + upDirectionThreshold = profile.UpDirectionThreshold; + maxDistanceSquare = profile.MaxDistance * profile.MaxDistance; + } + + private readonly LayerMask validLayers; + private readonly LayerMask invalidLayers; + private readonly float upDirectionThreshold; + private readonly float maxDistanceSquare; + + /// + public TeleportValidationResult IsValid(IPointerResult pointerResult, IMixedRealityTeleportHotSpot teleportHotSpot = null) + { + TeleportValidationResult teleportValidationResult; + + // Check distance. + if ((pointerResult.EndPoint - CameraCache.Main.transform.position).sqrMagnitude > maxDistanceSquare) + { + teleportValidationResult = TeleportValidationResult.Invalid; + } + // Check if it's in our valid layers + else if (((1 << pointerResult.CurrentPointerTarget.layer) & validLayers.value) != 0) + { + // See if it's a hot spot + if (teleportHotSpot != null && teleportHotSpot.IsActive) + { + teleportValidationResult = TeleportValidationResult.HotSpot; + } + else + { + // If it's NOT a hotspot, check if the hit normal is too steep + // (Hotspots override dot requirements) + teleportValidationResult = Vector3.Dot(pointerResult.LastRaycastHit.normal, Vector3.up) > upDirectionThreshold + ? TeleportValidationResult.Valid + : TeleportValidationResult.Invalid; + } + } + else if (((1 << pointerResult.CurrentPointerTarget.layer) & invalidLayers) != 0) + { + teleportValidationResult = TeleportValidationResult.Invalid; + } + else + { + teleportValidationResult = TeleportValidationResult.None; + } + + return teleportValidationResult; + } + } +} diff --git a/Runtime/Services/TeleportSystem/MixedRealityTeleportValidationDataProvider.cs.meta b/Runtime/Services/TeleportSystem/MixedRealityTeleportValidationDataProvider.cs.meta new file mode 100644 index 000000000..33a8d6e79 --- /dev/null +++ b/Runtime/Services/TeleportSystem/MixedRealityTeleportValidationDataProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3819bbcd819026b46874409b97116e61 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 5e1c8765530949369db753b5770399e9, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Services/TeleportSystem/TeleportMode.cs b/Runtime/Services/TeleportSystem/TeleportMode.cs deleted file mode 100644 index ff4c6ec9c..000000000 --- a/Runtime/Services/TeleportSystem/TeleportMode.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) XRTK. All rights reserved. -// Licensed under the MIT License. See LICENSE in the project root for license information. - -namespace XRTK.Services.Teleportation -{ - /// - /// Supported teleport modes in the implementation. - /// - public enum TeleportMode - { - /// - /// will instantly teleport the player - /// to the selected location and does not require any additional setup. - /// - Default = 0, - /// - /// mode lets 's - /// handle teleportation. This mode requires - /// to be configured with a concrete implementation. - /// - Provider - } -} diff --git a/Runtime/Services/TeleportSystem/TeleportValidationResult.cs b/Runtime/Services/TeleportSystem/TeleportValidationResult.cs new file mode 100644 index 000000000..e90ab9f2b --- /dev/null +++ b/Runtime/Services/TeleportSystem/TeleportValidationResult.cs @@ -0,0 +1,19 @@ +// Copyright (c) XRTK. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using System; + +namespace XRTK.Services.Teleportation +{ + /// + /// Possible validation outcomes by the . + /// + [Serializable] + public enum TeleportValidationResult + { + None = 0, + Valid, + Invalid, + HotSpot, + } +} diff --git a/Runtime/Definitions/Physics/TeleportSurfaceResult.cs.meta b/Runtime/Services/TeleportSystem/TeleportValidationResult.cs.meta similarity index 100% rename from Runtime/Definitions/Physics/TeleportSurfaceResult.cs.meta rename to Runtime/Services/TeleportSystem/TeleportValidationResult.cs.meta