Add toolkit.foundation package

This commit is contained in:
Santiago Lo Coco 2024-10-13 18:54:27 +02:00
parent eae9489ba8
commit 4902527289
4145 changed files with 694238 additions and 0 deletions

View File

@ -0,0 +1,21 @@
# Changelog
## 2.5.1
[MRTK 2.5.1 changes](https://github.com/microsoft/MixedRealityToolkit-Unity/milestone/15?closed=1)
## 2.5.0
[MRTK 2.5.0 changes](https://github.com/microsoft/MixedRealityToolkit-Unity/milestone/12?closed=1)
## 2.4.0
[MRTK 2.4.0 changes](https://github.com/microsoft/MixedRealityToolkit-Unity/milestone/11?closed=1)
## 2.3.0
[MRTK 2.3.0 changes](https://github.com/microsoft/MixedRealityToolkit-Unity/milestone/10?closed=1)
## 2.2.0
[MRTK 2.2.0 changes](https://github.com/microsoft/MixedRealityToolkit-Unity/milestone/9?closed=1)

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 4d3d415154baf7f429edd9dc550990e6
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.MixedReality.Toolkit.Tests.EditModeTests")]
[assembly: InternalsVisibleTo("Microsoft.MixedReality.Toolkit.Tests.PlayModeTests")]
[assembly: System.Reflection.AssemblyVersion("2.8.3.0")]
[assembly: System.Reflection.AssemblyFileVersion("2.8.3.0")]
[assembly: System.Reflection.AssemblyProduct("Microsoft® Mixed Reality Toolkit")]
[assembly: System.Reflection.AssemblyCopyright("Copyright © Microsoft Corporation")]

View File

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

View File

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

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Defines a documentation link for a service.
/// Used primarily by service inspector facades.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
[Obsolete("Use HelpURLAttribute from Unity instead")]
public class DocLinkAttribute : Attribute
{
public DocLinkAttribute(string url) { URL = url; }
public string URL { get; private set; }
}
}

View File

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

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// An attribute that allows a particular field to be rendered as multi-selectable
/// set of flags.
/// </summary>
/// <remarks>
/// From https://answers.unity.com/questions/486694/default-editor-enum-as-flags-.html
/// </remarks>
[AttributeUsage(AttributeTargets.Field)]
public sealed class EnumFlagsAttribute : PropertyAttribute
{
public EnumFlagsAttribute() { }
}
}

View File

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

View File

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// A PropertyAttribute for showing a warning box that the tagged implementation is experimental.
/// </summary>
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
public class ExperimentalAttribute : PropertyAttribute
{
/// <summary>
/// The text to display in the warning box.
/// </summary>
public string Text;
private const string defaultText = "<b><color=yellow>This is an experimental feature.</color></b>\n" +
"Parts of the MRTK appear to have a lot of value even if the details " +
"havent fully been fleshed out. For these types of features, we want " +
"the community to see them and get value out of them early. Because " +
"they are early in the cycle, we label them as experimental to indicate " +
"that they are still evolving, and subject to change over time.";
/// <summary>
/// Constructor.
/// </summary>
/// <param name="experimentalText">The experimental text to display in the warning box.</param>
public ExperimentalAttribute(string experimentalText = defaultText)
{
Text = experimentalText;
}
}
}

View File

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

View File

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
#if WINDOWS_UWP && !ENABLE_IL2CPP
using Microsoft.MixedReality.Toolkit;
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Constraint that allows selection of classes that extend a specific class when
/// selecting a <see cref="Utilities.SystemType"/> with the Unity inspector.
/// </summary>
public sealed class ExtendsAttribute : SystemTypeAttribute
{
/// <summary>
/// Gets the type of class that selectable classes must derive from.
/// </summary>
public Type BaseType { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="ExtendsAttribute"/> class.
/// </summary>
/// <param name="baseType">Type of class that selectable classes must derive from.</param>
/// <param name="grouping">Gets or sets grouping of selectable classes. Defaults to <see cref="Utilities.TypeGrouping.ByNamespaceFlat"/> unless explicitly specified.</param>
public ExtendsAttribute(Type baseType, TypeGrouping grouping) : base(baseType, grouping)
{
BaseType = baseType;
}
/// <inheritdoc/>
public override bool IsConstraintSatisfied(Type type)
{
return base.IsConstraintSatisfied(type) &&
BaseType.IsAssignableFrom(type) &&
type != BaseType;
}
}
}

View File

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

View File

@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// A PropertyAttribute for showing a collapsible Help section.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class HelpAttribute : PropertyAttribute
{
/// <summary>
/// The help text
/// </summary>
public string Text;
/// <summary>
/// The help header foldout text
/// </summary>
/// <remarks>
/// If Collapsible is false, then this header text will not be shown.
/// </remarks>
public string Header;
/// <summary>
/// If true, this will be a collapsible help section. Defaults to true.
/// </summary>
public bool Collapsible;
/// <summary>
/// Constructor
/// </summary>
/// <param name="helpText">The help text to display</param>
/// <param name="helpHeader">The help header foldout text</param>
/// <param name="collapsible">If true, this help drawer will be collapsible</param>
public HelpAttribute(string helpText, string helpHeader = "Help", bool collapsible = true)
{
Text = helpText;
Header = helpHeader;
Collapsible = collapsible;
}
}
}

View File

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

View File

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
#if WINDOWS_UWP && !ENABLE_IL2CPP
using Microsoft.MixedReality.Toolkit;
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Constraint that allows selection of classes that implement a specific interface
/// when selecting a <see cref="Utilities.SystemType"/> with the Unity inspector.
/// </summary>
public sealed class ImplementsAttribute : SystemTypeAttribute
{
/// <summary>
/// Gets the type of interface that selectable classes must implement.
/// </summary>
public Type InterfaceType { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="ImplementsAttribute"/> class.
/// </summary>
/// <param name="interfaceType">Type of interface that selectable classes must implement.</param>
/// <param name="grouping">Gets or sets grouping of selectable classes. Defaults to <see cref="Utilities.TypeGrouping.ByNamespaceFlat"/> unless explicitly specified.</param>
public ImplementsAttribute(Type interfaceType, TypeGrouping grouping) : base(interfaceType, grouping)
{
InterfaceType = interfaceType;
}
/// <inheritdoc />
public override bool IsConstraintSatisfied(Type type)
{
if (base.IsConstraintSatisfied(type))
{
var interfaces = type.GetInterfaces();
for (var i = 0; i < interfaces.Length; i++)
{
if (interfaces[i] == InterfaceType)
{
return true;
}
}
}
return false;
}
}
}

View File

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

View File

@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Linq;
#if WINDOWS_UWP && !ENABLE_IL2CPP
using System.Reflection;
using Microsoft.MixedReality.Toolkit;
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Attach to a controller device class to make it show up in the controller mapping profile.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class MixedRealityControllerAttribute : Attribute
{
/// <summary>
/// The SupportedControllerType to which the controller device belongs to.
/// </summary>
public SupportedControllerType SupportedControllerType { get; }
/// <summary>
/// List of handedness values supported by the respective controller.
/// </summary>
public Handedness[] SupportedHandedness { get; }
/// <summary>
/// Path to image file used when displaying an icon in the UI.
/// </summary>
public string TexturePath { get; }
/// <summary>
/// Additional flags for configuring controller capabilities.
/// </summary>
public MixedRealityControllerConfigurationFlags Flags { get; }
/// <summary>
/// The supported Unity XR pipelines for this controller.
/// </summary>
public SupportedUnityXRPipelines SupportedUnityXRPipelines { get; }
/// <summary>
/// Constructor.
/// </summary>
public MixedRealityControllerAttribute(
SupportedControllerType supportedControllerType,
Handedness[] supportedHandedness,
string texturePath = "",
MixedRealityControllerConfigurationFlags flags = 0,
SupportedUnityXRPipelines supportedUnityXRPipelines = (SupportedUnityXRPipelines)(-1))
{
SupportedControllerType = supportedControllerType;
SupportedHandedness = supportedHandedness;
TexturePath = texturePath;
Flags = flags;
SupportedUnityXRPipelines = supportedUnityXRPipelines;
}
/// <summary>
/// Convenience function for retrieving the attribute given a certain class type.
/// </summary>
public static MixedRealityControllerAttribute Find(Type type)
{
return type.GetCustomAttributes(typeof(MixedRealityControllerAttribute), true).FirstOrDefault() as MixedRealityControllerAttribute;
}
}
}

View File

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

View File

@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attribute that defines the properties of a Mixed Reality Toolkit data provider.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MixedRealityDataProviderAttribute : MixedRealityExtensionServiceAttribute
{
/// <summary>
/// The interface type of the IMixedRealityService for which the data provider is supported.
/// </summary>
public Type ServiceInterfaceType { get; }
/// <summary>
/// The supported Unity XR pipelines for this data provider.
/// </summary>
public SupportedUnityXRPipelines SupportedUnityXRPipelines { get; }
public MixedRealityDataProviderAttribute(
Type serviceInterfaceType,
SupportedPlatforms runtimePlatforms,
string name = "",
string profilePath = "",
string packageFolder = "MixedRealityToolkit",
bool requiresProfile = false,
SupportedUnityXRPipelines supportedUnityXRPipelines = (SupportedUnityXRPipelines)(-1))
: base(runtimePlatforms, name, profilePath, packageFolder, requiresProfile)
{
ServiceInterfaceType = serviceInterfaceType;
SupportedUnityXRPipelines = supportedUnityXRPipelines;
}
}
}

View File

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

View File

@ -0,0 +1,109 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
using Microsoft.MixedReality.Toolkit.Utilities.Editor;
using UnityEditor;
#endif
#if WINDOWS_UWP && !ENABLE_IL2CPP
using Microsoft.MixedReality.Toolkit;
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attribute that defines the properties of a Mixed Reality Toolkit extension service.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MixedRealityExtensionServiceAttribute : Attribute
{
/// <summary>
/// The friendly name for this service.
/// </summary>
public virtual string Name { get; }
/// <summary>
/// The runtime platform(s) to run this service.
/// </summary>
public virtual SupportedPlatforms RuntimePlatforms { get; }
/// <summary>
/// Is a profile explicitly required?
/// </summary>
public virtual bool RequiresProfile { get; }
/// <summary>
/// The file path to the default profile asset relative to the package folder.
/// </summary>
public virtual string DefaultProfilePath { get; }
/// <summary>
/// The package where the default profile asset resides.
/// </summary>
public virtual string PackageFolder { get; }
/// <summary>
/// The default profile.
/// </summary>
public virtual BaseMixedRealityProfile DefaultProfile
{
get
{
#if UNITY_EDITOR
MixedRealityToolkitModuleType moduleType = MixedRealityToolkitFiles.GetModuleFromPackageFolder(PackageFolder);
if (moduleType != MixedRealityToolkitModuleType.None)
{
string folder = MixedRealityToolkitFiles.MapModulePath(moduleType);
if (!string.IsNullOrWhiteSpace(folder))
{
return AssetDatabase.LoadAssetAtPath<BaseMixedRealityProfile>(System.IO.Path.Combine(folder, DefaultProfilePath));
}
}
else if (EditorProjectUtilities.FindRelativeDirectory(PackageFolder, out string folder))
{
return AssetDatabase.LoadAssetAtPath<BaseMixedRealityProfile>(System.IO.Path.Combine(folder, DefaultProfilePath));
}
// If we get here, there was an issue finding the profile.
Debug.LogError("Unable to find or load the profile.");
#endif
return null;
}
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="runtimePlatforms">The platforms on which the extension service is supported.</param>
/// <param name="defaultProfilePath">The relative path to the default profile asset.</param>
/// <param name="packageFolder">The package folder to which the path is relative.</param>
public MixedRealityExtensionServiceAttribute(
SupportedPlatforms runtimePlatforms,
string name = "",
string defaultProfilePath = "",
string packageFolder = "MixedRealityToolkit",
bool requiresProfile = false)
{
RuntimePlatforms = runtimePlatforms;
Name = name;
DefaultProfilePath = defaultProfilePath;
PackageFolder = packageFolder;
RequiresProfile = requiresProfile;
}
/// <summary>
/// Convenience function for retrieving the attribute given a certain class type.
/// </summary>
public static MixedRealityExtensionServiceAttribute Find(Type type)
{
return type.GetCustomAttributes(typeof(MixedRealityExtensionServiceAttribute), true).FirstOrDefault() as MixedRealityExtensionServiceAttribute;
}
}
}

View File

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

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attach to a class implementing IMixedRealityServiceInspector to generate a facade inspector.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class MixedRealityServiceInspectorAttribute : Attribute
{
public MixedRealityServiceInspectorAttribute(Type serviceType)
{
if (!typeof(IMixedRealityService).IsAssignableFrom(serviceType))
throw new Exception("Can't use this attribute with type " + serviceType + " - service must implement " + typeof(IMixedRealityService) + " interface.");
ServiceType = serviceType;
}
public Type ServiceType { get; private set; }
}
}

View File

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

View File

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attribute that defines which service a profile is meant to be consumed by.
/// Only applies to profiles that are consumed by types implementing IMixedRealityService.
/// A service must implement all required types and no excluded types to be considered compatible with the profile.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class MixedRealityServiceProfileAttribute : Attribute
{
public MixedRealityServiceProfileAttribute(Type requiredType, Type excludedType = null)
{
RequiredTypes = new Type[] { requiredType };
ExcludedTypes = excludedType != null ? new Type[] { excludedType } : new Type[0];
}
public MixedRealityServiceProfileAttribute(Type[] requiredTypes, Type[] excludedTypes = null)
{
RequiredTypes = requiredTypes;
ExcludedTypes = excludedTypes ?? (new Type[0]);
}
public Type[] RequiredTypes { get; private set; }
public Type[] ExcludedTypes { get; private set; }
}
}

View File

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

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Physics
{
/// <summary>
/// Attribute used to make an <see cref="int"/> field render a dropdown generated from the current layers defined in the Tag Manager.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class PhysicsLayerAttribute : PropertyAttribute
{
public PhysicsLayerAttribute() { }
}
}

View File

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

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attribute used to ensure that a GameObject inspector slot only accepts prefabs.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class PrefabAttribute : PropertyAttribute { }
}

View File

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

View File

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
public class ReadOnlyAttribute : PropertyAttribute { }
public class BeginReadOnlyGroupAttribute : PropertyAttribute { }
public class EndReadOnlyGroupAttribute : PropertyAttribute { }
}

View File

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

View File

@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attribute to mark up an int field to be drawn using the
/// ScenePickPropertyDrawer
/// This allows the UI to display a dropdown instead of a
/// numeric entry field.
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class ScenePickAttribute : PropertyAttribute
{
// Nothing to see Here, This only acts as a marker to help the editor.
}
}

View File

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

View File

@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attribute used to display a dropdown of registered keywords from the speech profile.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class SpeechKeywordAttribute : PropertyAttribute { }
}

View File

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

View File

@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
#if WINDOWS_UWP && !ENABLE_IL2CPP
using Microsoft.MixedReality.Toolkit;
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Base class for class selection constraints that can be applied when selecting
/// a <see cref="Utilities.SystemType"/> with the Unity inspector.
/// </summary>
public abstract class SystemTypeAttribute : PropertyAttribute
{
/// <summary>
/// Gets or sets grouping of selectable classes. Defaults to <see cref="Utilities.TypeGrouping.ByNamespaceFlat"/> unless explicitly specified.
/// </summary>
public TypeGrouping Grouping { get; protected set; }
/// <summary>
/// Gets or sets whether abstract classes can be selected from drop-down.
/// Defaults to a value of <c>false</c> unless explicitly specified.
/// </summary>
public bool AllowAbstract { get; protected set; } = false;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="type">Initializes a new instance of the <see cref="SystemTypeAttribute"/> class.</param>
/// <param name="grouping">Gets or sets grouping of selectable classes. Defaults to <see cref="Utilities.TypeGrouping.ByNamespaceFlat"/> unless explicitly specified.</param>
protected SystemTypeAttribute(Type type, TypeGrouping grouping = TypeGrouping.ByNamespaceFlat)
{
#if WINDOWS_UWP && !ENABLE_IL2CPP
bool isValid = type.IsClass() || type.IsInterface() || type.IsValueType() && !type.IsEnum();
#else
bool isValid = type.IsClass || type.IsInterface || type.IsValueType && !type.IsEnum;
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
if (!isValid)
{
Debug.Assert(isValid, $"Invalid Type {type} in attribute.");
}
Grouping = grouping;
}
/// <summary>
/// Determines whether the specified <see cref="System.Type"/> satisfies filter constraint.
/// </summary>
/// <param name="type">Type to test.</param>
/// <returns>
/// A <see cref="bool"/> value indicating if the type specified by <paramref name="type"/>
/// satisfies this constraint and should thus be selectable.
/// </returns>
public virtual bool IsConstraintSatisfied(Type type)
{
#if WINDOWS_UWP && !ENABLE_IL2CPP
return AllowAbstract || !type.IsAbstract();
#else
return AllowAbstract || !type.IsAbstract;
#endif // WINDOWS_UWP && !ENABLE_IL2CPP
}
}
}

View File

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

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// A PropertyAttribute for Unity tags (a string field).
/// </summary>
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class TagPropertyAttribute : PropertyAttribute
{
// Do nothing
}
}

View File

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

View File

@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Attribute used to make a float or int variable in a script be restricted to a specific range.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class Vector3RangeAttribute : PropertyAttribute
{
/// <summary>
/// Minimum value.
/// </summary>
public readonly float Min;
/// <summary>
/// Maximum value.
/// </summary>
public readonly float Max;
/// <summary>
/// Attribute used to make a float or int variable in a script be restricted to a specific range.
/// </summary>
/// <param name="min">The minimum allowed value.</param>
/// <param name="max">The maximum allowed value.</param>
public Vector3RangeAttribute(float min, float max)
{
Min = min;
Max = max;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// Base abstract class for all Mixed Reality profile configurations.
/// Extends ScriptableObject and used as a property container to initialize MRTK services.
/// </summary>
[Serializable]
public abstract class BaseMixedRealityProfile : ScriptableObject
{
[SerializeField]
[HideInInspector]
private bool isCustomProfile = true;
internal bool IsCustomProfile => isCustomProfile;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7bf26942d2da73b49a9ae41a021e6ea6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.MixedReality.Toolkit.Boundary
{
/// <summary>
/// Defines different types of boundaries that can be requested.
/// </summary>
public enum BoundaryType
{
/// <summary>
/// A rectangular area calculated as the largest rectangle within the tracked area, good for placing content near the user.
/// </summary>
PlayArea,
/// <summary>
/// The full tracked boundary, typically manually drawn by a user while setting up their device.
/// </summary>
TrackedArea
}
}

View File

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

View File

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Boundary
{
/// <summary>
/// The Edge structure defines the points of a line segment that are used to
/// construct a polygonal boundary.
/// </summary>
public struct Edge
{
/// <summary>
/// The first point of the edge line segment.
/// </summary>
public readonly Vector2 PointA;
/// <summary>
/// The second point of the edge line segment.
/// </summary>
public readonly Vector2 PointB;
/// <summary>
/// Initializes the Edge structure.
/// </summary>
/// <param name="pointA">The first point of the line segment.</param>
/// <param name="pointB">The second point of the line segment.</param>
public Edge(Vector2 pointA, Vector2 pointB)
{
PointA = pointA;
PointB = pointB;
}
/// <summary>
/// Initializes the Edge structure.
/// </summary>
/// <param name="pointA">The first point of the line segment.</param>
/// <param name="pointB">The second point of the line segment.</param>
public Edge(Vector3 pointA, Vector3 pointB) :
// Use the X and Z parameters as our edges are height agnostic.
this(new Vector2(pointA.x, pointA.z), new Vector2(pointB.x, pointB.z))
{ }
}
}

View File

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

View File

@ -0,0 +1,563 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Boundary
{
/// <summary>
/// The InscribedRectangle class defines the largest rectangle within an
/// arbitrary shape.
/// </summary>
public class InscribedRectangle
{
/// <summary>
/// Total number of starting points randomly generated within the boundary.
/// </summary>
private const int randomPointCount = 30;
/// <summary>
/// The total amount of height, in meters, we want to gain with each binary search
/// change before we decide that it's good enough.
/// </summary>
private const float minimumHeightGain = 0.01f;
/// <summary>
/// Angles to use for fitting the rectangle within the boundary.
/// </summary>
private static readonly float[] FitAngles = { 0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165 };
/// <summary>
/// Aspect ratios used when fitting rectangles within the boundary.
/// </summary>
private static readonly float[] AspectRatios = {
1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f,
5.0f, 5.5f, 6, 6.5f, 7, 7.5f, 8.0f, 8.5f, 9.0f,
9.5f, 10.0f, 10.5f, 11.0f, 11.5f, 12.0f, 12.5f,
13.0f, 13.5f, 14.0f, 14.5f, 15.0f};
/// <summary>
/// The center point of the inscribed rectangle.
/// </summary>
public Vector2 Center { get; private set; } = EdgeUtilities.InvalidPoint;
/// <summary>
/// The width of the inscribed rectangle.
/// </summary>
public float Width { get; private set; } = 0f;
/// <summary>
/// The height of the inscribed rectangle.
/// </summary>
public float Height { get; private set; } = 0f;
/// <summary>
/// The rotation angle, in degrees, of the inscribed rectangle.
/// </summary>
public float Angle { get; private set; } = 0f;
/// <summary>
/// Is the described rectangle valid?
/// </summary>
/// <remarks>
/// A rectangle is considered valid if its center point is valid.
/// </remarks>
public bool IsValid => EdgeUtilities.IsValidPoint(Center);
/// <summary>
/// Finds a large inscribed rectangle. Tries to be maximal but this is
/// best effort. The algorithm used was inspired by the blog post
/// https://d3plus.org/blog/behind-the-scenes/2014/07/08/largest-rect/
/// Random points within the polygon are chosen, and then 2 lines are
/// drawn through those points. The midpoints of those lines are
/// used as the center of various rectangles, using a binary search to
/// vary the size, until the largest fit-able rectangle is found.
/// This is then repeated for predefined angles (0-180 in steps of 15)
/// and aspect ratios (1 to 15 in steps of 0.5).
/// </summary>
/// <param name="geometryEdges">The boundary geometry.</param>
/// <param name="randomSeed">Random number generator seed.</param>
/// <remarks>
/// For the most reproducible results, use the same randomSeed value each time this method is called.
/// </remarks>
public InscribedRectangle(Edge[] geometryEdges, int randomSeed)
{
if (geometryEdges == null || geometryEdges.Length == 0)
{
Debug.LogError("InscribedRectangle requires an array of Edges. You passed in a null or empty array.");
return;
}
// Clear previous rectangle
Center = EdgeUtilities.InvalidPoint;
Width = 0;
Height = 0;
Angle = 0;
float minX = EdgeUtilities.maxWidth;
float minY = EdgeUtilities.maxWidth;
float maxX = -EdgeUtilities.maxWidth;
float maxY = -EdgeUtilities.maxWidth;
// Find min x, min y, max x, max y
for (int i = 0; i < geometryEdges.Length; i++)
{
Edge edge = geometryEdges[i];
if ((edge.PointA.x < minX) || (edge.PointB.x < minX))
{
minX = Mathf.Min(edge.PointA.x, edge.PointB.x);
}
if ((edge.PointA.y < minY) || (edge.PointB.y < minY))
{
minY = Mathf.Min(edge.PointA.y, edge.PointB.y);
}
if ((edge.PointA.x > maxX) || (edge.PointB.x > maxX))
{
maxX = Mathf.Max(edge.PointA.x, edge.PointB.x);
}
if ((edge.PointA.y > maxY) || (edge.PointB.y > maxY))
{
maxY = Mathf.Max(edge.PointA.y, edge.PointB.y);
}
}
// Generate random points until we have randomPointCount starting points
Vector2[] startingPoints = new Vector2[randomPointCount];
{
System.Random random = new System.Random(randomSeed);
for (int i = 0; i < startingPoints.Length; i++)
{
Vector2 candidatePoint;
do
{
candidatePoint.x = ((float)random.NextDouble() * (maxX - minX)) + minX;
candidatePoint.y = ((float)random.NextDouble() * (maxY - minY)) + minY;
}
while (!EdgeUtilities.IsInsideBoundary(geometryEdges, candidatePoint));
startingPoints[i] = candidatePoint;
}
}
for (int angleIndex = 0; angleIndex < FitAngles.Length; angleIndex++)
{
for (int pointIndex = 0; pointIndex < startingPoints.Length; pointIndex++)
{
Vector2 topCollisionPoint;
Vector2 bottomCollisionPoint;
Vector2 leftCollisionPoint;
Vector2 rightCollisionPoint;
float angleRadians = MathUtilities.DegreesToRadians(FitAngles[angleIndex]);
// Find the collision point of a cross through the given point at the given angle.
// Note, we are ignoring the return value as we are checking each point's validity
// individually.
FindSurroundingCollisionPoints(
geometryEdges,
startingPoints[pointIndex],
angleRadians,
out topCollisionPoint,
out bottomCollisionPoint,
out leftCollisionPoint,
out rightCollisionPoint);
float newWidth;
float newHeight;
if (EdgeUtilities.IsValidPoint(topCollisionPoint) && EdgeUtilities.IsValidPoint(bottomCollisionPoint))
{
float aX = topCollisionPoint.x;
float aY = topCollisionPoint.y;
float bX = bottomCollisionPoint.x;
float bY = bottomCollisionPoint.y;
// Calculate the midpoint between the top and bottom collision points.
Vector2 verticalMidpoint = new Vector2((aX + bX) * 0.5f, (aY + bY) * 0.5f);
if (TryFixMaximumRectangle(
geometryEdges,
verticalMidpoint,
angleRadians,
Width * Height,
out newWidth,
out newHeight))
{
Center = verticalMidpoint;
Angle = FitAngles[angleIndex];
Width = newWidth;
Height = newHeight;
}
}
if (EdgeUtilities.IsValidPoint(leftCollisionPoint) && EdgeUtilities.IsValidPoint(rightCollisionPoint))
{
float aX = leftCollisionPoint.x;
float aY = leftCollisionPoint.y;
float bX = rightCollisionPoint.x;
float bY = rightCollisionPoint.y;
// Calculate the midpoint between the left and right collision points.
Vector2 horizontalMidpoint = new Vector2((aX + bX) * 0.5f, (aY + bY) * 0.5f);
if (TryFixMaximumRectangle(
geometryEdges,
horizontalMidpoint,
angleRadians,
Width * Height,
out newWidth,
out newHeight))
{
Center = horizontalMidpoint;
Angle = FitAngles[angleIndex];
Width = newWidth;
Height = newHeight;
}
}
}
}
}
/// <summary>
/// Find points at which there are collisions with the geometry around a given point.
/// </summary>
/// <param name="geometryEdges">The boundary geometry.</param>
/// <param name="point">The point around which collisions will be identified.</param>
/// <param name="angleRadians">The angle, in radians, at which the collision points will be oriented.</param>
/// <param name="topCollisionPoint">Receives the coordinates of the upper collision point.</param>
/// <param name="bottomCollisionPoint">Receives the coordinates of the lower collision point.</param>
/// <param name="leftCollisionPoint">Receives the coordinates of the left collision point.</param>
/// <param name="rightCollisionPoint">Receives the coordinates of the right collision point.</param>
/// <returns>
/// True if all of the required collision points are located, false otherwise.
/// If a point is unable to be found, the appropriate out parameter will be set to <see cref="EdgeUtilities.InvalidPoint"/>.
/// </returns>
private bool FindSurroundingCollisionPoints(
Edge[] geometryEdges,
Vector2 point,
float angleRadians,
out Vector2 topCollisionPoint,
out Vector2 bottomCollisionPoint,
out Vector2 leftCollisionPoint,
out Vector2 rightCollisionPoint)
{
// Initialize out parameters.
topCollisionPoint = EdgeUtilities.InvalidPoint;
bottomCollisionPoint = EdgeUtilities.InvalidPoint;
leftCollisionPoint = EdgeUtilities.InvalidPoint;
rightCollisionPoint = EdgeUtilities.InvalidPoint;
// Check to see if the point is inside the geometry.
if (!EdgeUtilities.IsInsideBoundary(geometryEdges, point))
{
return false;
}
// Define values that are outside of the maximum boundary size.
float largeValue = EdgeUtilities.maxWidth;
float smallValue = -largeValue;
// Find the top and bottom collision points by creating a large line segment that goes through the point to MAX and MIN values on Y
Vector2 topEndpoint = new Vector2(point.x, largeValue);
Vector2 bottomEndpoint = new Vector2(point.x, smallValue);
topEndpoint = RotatePoint(topEndpoint, point, angleRadians);
bottomEndpoint = RotatePoint(bottomEndpoint, point, angleRadians);
Edge verticalLine = new Edge(topEndpoint, bottomEndpoint);
// Find the left and right collision points by creating a large line segment that goes through the point to MAX and Min values on X
Vector2 rightEndpoint = new Vector2(largeValue, point.y);
Vector2 leftEndpoint = new Vector2(smallValue, point.y);
rightEndpoint = RotatePoint(rightEndpoint, point, angleRadians);
leftEndpoint = RotatePoint(leftEndpoint, point, angleRadians);
Edge horizontalLine = new Edge(rightEndpoint, leftEndpoint);
for (int i = 0; i < geometryEdges.Length; i++)
{
// Look for a vertical collision
Vector2 verticalIntersectionPoint = EdgeUtilities.GetIntersectionPoint(geometryEdges[i], verticalLine);
if (EdgeUtilities.IsValidPoint(verticalIntersectionPoint))
{
// Is the intersection above or below the point?
if (RotatePoint(verticalIntersectionPoint, point, -angleRadians).y > point.y)
{
// Update the top collision point
if (!EdgeUtilities.IsValidPoint(topCollisionPoint) ||
(Vector2.SqrMagnitude(point - verticalIntersectionPoint) < Vector2.SqrMagnitude(point - topCollisionPoint)))
{
topCollisionPoint = verticalIntersectionPoint;
}
}
else
{
// Update the bottom collision point
if (!EdgeUtilities.IsValidPoint(bottomCollisionPoint) ||
(Vector2.SqrMagnitude(point - verticalIntersectionPoint) < Vector2.SqrMagnitude(point - bottomCollisionPoint)))
{
bottomCollisionPoint = verticalIntersectionPoint;
}
}
}
// Look for a horizontal collision
Vector2 horizontalIntersection = EdgeUtilities.GetIntersectionPoint(geometryEdges[i], horizontalLine);
if (EdgeUtilities.IsValidPoint(horizontalIntersection))
{
// Is this intersection to the left or the right of the point?
if (RotatePoint(horizontalIntersection, point, -angleRadians).x < point.x)
{
// Update the left collision point
if (!EdgeUtilities.IsValidPoint(leftCollisionPoint) ||
(Vector2.SqrMagnitude(point - horizontalIntersection) < Vector2.SqrMagnitude(point - leftCollisionPoint)))
{
leftCollisionPoint = horizontalIntersection;
}
}
else
{
// Update the right collision point
if (!EdgeUtilities.IsValidPoint(rightCollisionPoint) ||
(Vector2.SqrMagnitude(point - horizontalIntersection) < Vector2.SqrMagnitude(point - rightCollisionPoint)))
{
rightCollisionPoint = horizontalIntersection;
}
}
}
}
// Each corner of the rectangle must intersect with the geometry.
if (!EdgeUtilities.IsValidPoint(topCollisionPoint) ||
!EdgeUtilities.IsValidPoint(bottomCollisionPoint) ||
!EdgeUtilities.IsValidPoint(leftCollisionPoint) ||
!EdgeUtilities.IsValidPoint(rightCollisionPoint))
{
return false;
}
return true;
}
/// <summary>
/// Determine of the provided point lies within the defined rectangle.
/// </summary>
/// <param name="point">The point to check</param>
/// <returns>
/// True if the point is within the rectangle's bounds, false otherwise.
/// </returns>
/// <exception cref="System.InvalidOperationException">The rectangle is not valid.</exception>
public bool IsInsideBoundary(Vector2 point)
{
if (!IsValid)
{
throw new InvalidOperationException("A point cannot be within an invalid rectangle.");
}
point -= Center;
point = RotatePoint(point, Vector2.zero, MathUtilities.DegreesToRadians(-Angle));
bool inWidth = Mathf.Abs(point.x) <= (Width * 0.5f);
bool inHeight = Mathf.Abs(point.y) <= (Height * 0.5f);
return (inWidth && inHeight);
}
/// <summary>
/// Rotate a two dimensional point about another point by the specified angle.
/// </summary>
/// <param name="point">The point to be rotated.</param>
/// <param name="origin">The point about which the rotation is to occur.</param>
/// <param name="angleRadians">The angle for the rotation, in radians</param>
/// <returns>
/// The coordinates of the rotated point.
/// </returns>
private Vector2 RotatePoint(Vector2 point, Vector2 origin, float angleRadians)
{
if (angleRadians.Equals(0f))
{
return point;
}
Vector2 rotated = point;
// Translate to origin of rotation
rotated.x -= origin.x;
rotated.y -= origin.y;
// Rotate the point
float sin = Mathf.Sin(angleRadians);
float cos = Mathf.Cos(angleRadians);
float x = rotated.x * cos - rotated.y * sin;
float y = rotated.x * sin + rotated.y * cos;
// Translate back and return
rotated.x = x + origin.x;
rotated.y = y + origin.y;
return rotated;
}
/// <summary>
/// Check to see if a rectangle centered at the specified point and oriented at
/// the specified angle will fit within the geometry.
/// </summary>
/// <param name="geometryEdges">The boundary geometry.</param>
/// <param name="centerPoint">The center point of the rectangle.</param>
/// <param name="angleRadians">The orientation, in radians, of the rectangle.</param>
/// <param name="width">The width of the rectangle.</param>
/// <param name="height">The height of the rectangle.</param>
private bool CheckRectangleFit(
Edge[] geometryEdges,
Vector2 centerPoint,
float angleRadians,
float width,
float height)
{
float halfWidth = width * 0.5f;
float halfHeight = height * 0.5f;
// Calculate the rectangle corners.
Vector2 topLeft = new Vector2(centerPoint.x - halfWidth, centerPoint.y + halfHeight);
Vector2 topRight = new Vector2(centerPoint.x + halfWidth, centerPoint.y + halfHeight);
Vector2 bottomLeft = new Vector2(centerPoint.x - halfWidth, centerPoint.y - halfHeight);
Vector2 bottomRight = new Vector2(centerPoint.x + halfWidth, centerPoint.y - halfHeight);
// Rotate the rectangle.
topLeft = RotatePoint(topLeft, centerPoint, angleRadians);
topRight = RotatePoint(topRight, centerPoint, angleRadians);
bottomLeft = RotatePoint(bottomLeft, centerPoint, angleRadians);
bottomRight = RotatePoint(bottomRight, centerPoint, angleRadians);
// Get the rectangle edges.
Edge topEdge = new Edge(topLeft, topRight);
Edge rightEdge = new Edge(topRight, bottomRight);
Edge bottomEdge = new Edge(bottomLeft, bottomRight);
Edge leftEdge = new Edge(topLeft, bottomLeft);
// Check for collisions with the boundary geometry. If any of our edges collide,
// the rectangle will not fit within the playspace.
for (int i = 0; i < geometryEdges.Length; i++)
{
if (EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], topEdge)) ||
EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], rightEdge)) ||
EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], bottomEdge)) ||
EdgeUtilities.IsValidPoint(EdgeUtilities.GetIntersectionPoint(geometryEdges[i], leftEdge)))
{
return false;
}
}
// No collisions found with the rectangle. Success!
return true;
}
/// <summary>
/// Attempt to fit the largest rectangle possible within the geometry.
/// </summary>
/// <param name="geometryEdges">The boundary geometry.</param>
/// <param name="centerPoint">The center point for the rectangle.</param>
/// <param name="angleRadians">The rotation, in radians, of the rectangle.</param>
/// <param name="minArea">The smallest allowed area.</param>
/// <param name="width">Returns the width of the rectangle.</param>
/// <param name="height">Returns the height of the rectangle.</param>
/// <returns>
/// True if a rectangle with an area greater than or equal to minArea was able to be fit
/// within the geometry at centerPoint.
/// </returns>
private bool TryFixMaximumRectangle(
Edge[] geometryEdges,
Vector2 centerPoint,
float angleRadians,
float minArea,
out float width,
out float height)
{
width = 0.0f;
height = 0.0f;
Vector2 topCollisionPoint;
Vector2 bottomCollisionPoint;
Vector2 leftCollisionPoint;
Vector2 rightCollisionPoint;
// Find the collision points with the geometry
if (!FindSurroundingCollisionPoints(geometryEdges, centerPoint, angleRadians,
out topCollisionPoint, out bottomCollisionPoint, out leftCollisionPoint, out rightCollisionPoint))
{
return false;
}
// Start by calculating max width and height by ray-casting a cross from the point at the given angle
// and taking the shortest leg of each ray. Width is the longest.
float verticalMinDistanceToEdge = Mathf.Min(
Vector2.Distance(centerPoint, topCollisionPoint),
Vector2.Distance(centerPoint, bottomCollisionPoint));
float horizontalMinDistanceToEdge = Mathf.Min(
Vector2.Distance(centerPoint, leftCollisionPoint),
Vector2.Distance(centerPoint, rightCollisionPoint));
// Width is the largest of the possible dimensions
float maxWidth = Math.Max(verticalMinDistanceToEdge, horizontalMinDistanceToEdge) * 2.0f;
float maxHeight = Math.Min(verticalMinDistanceToEdge, horizontalMinDistanceToEdge) * 2.0f;
float aspectRatio = 0.0f;
// For each aspect ratio we do a binary search to find the maximum rectangle that fits,
// though once we start increasing our area by minimumHeightGain we call it good enough.
for (int i = 0; i < AspectRatios.Length; i++)
{
// The height is limited by the width. If a height would make our width exceed maxWidth, it can't be used
float searchHeightUpperBound = Mathf.Max(maxHeight, maxWidth / AspectRatios[i]);
// Set to the min height that will out perform our previous area at the given aspect ratio. This is 0 the first time.
// Derived from biggestAreaSoFar=height*(height*aspectRatio)
float searchHeightLowerBound = Mathf.Sqrt(Mathf.Max((width * height), minArea) / AspectRatios[i]);
// If the lowest value needed to outperform the previous best is greater than our max,
// this aspect ratio can't outperform what we've already calculated.
if ((searchHeightLowerBound > searchHeightUpperBound) ||
(searchHeightLowerBound * AspectRatios[i] > maxWidth))
{
continue;
}
float currentTestingHeight = Mathf.Max(searchHeightLowerBound, maxHeight * 0.5f);
// Perform the binary search until continuing to search will not give us a significant win.
do
{
if (CheckRectangleFit(geometryEdges,
centerPoint,
angleRadians,
AspectRatios[i] * currentTestingHeight,
currentTestingHeight))
{
// Binary search up-ward
// If the rectangle will fit, increase the lower bounds of our binary search
searchHeightLowerBound = currentTestingHeight;
width = currentTestingHeight * AspectRatios[i];
height = currentTestingHeight;
aspectRatio = AspectRatios[i];
currentTestingHeight = (searchHeightUpperBound + currentTestingHeight) * 0.5f;
}
else
{
// If the rectangle won't fit, update our upper bound and lower our binary search
searchHeightUpperBound = currentTestingHeight;
currentTestingHeight = (currentTestingHeight + searchHeightLowerBound) * 0.5f;
}
}
while ((searchHeightUpperBound - searchHeightLowerBound) > minimumHeightGain);
}
return (aspectRatio > 0.0f);
}
}
}

View File

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

View File

@ -0,0 +1,201 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Physics;
using Microsoft.MixedReality.Toolkit.Utilities;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Boundary
{
/// <summary>
/// Configuration profile settings for setting up boundary visualizations.
/// </summary>
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Boundary Visualization Profile", fileName = "MixedRealityBoundaryVisualizationProfile", order = (int)CreateProfileMenuItemIndices.BoundaryVisualization)]
[MixedRealityServiceProfile(typeof(IMixedRealityBoundarySystem))]
[HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/features/boundary/boundary-system-getting-started")]
public class MixedRealityBoundaryVisualizationProfile : BaseMixedRealityProfile
{
[SerializeField]
[Tooltip("The approximate height of the play space, in meters.")]
private float boundaryHeight = 3.0f;
/// <summary>
/// The developer defined height of the boundary, in meters.
/// </summary>
/// <remarks>
/// The BoundaryHeight property is used to create a three dimensional volume for the play space.
/// </remarks>
public float BoundaryHeight => boundaryHeight;
#region Floor settings
[SerializeField]
[Tooltip("Should the floor be displayed in the scene?")]
private bool showFloor = true;
/// <summary>
/// Should the boundary system display the floor?
/// </summary>
public bool ShowFloor => showFloor;
// todo: consider allowing optional custom prefab
[SerializeField]
[Tooltip("The material to use when displaying the floor.")]
private Material floorMaterial = null;
/// <summary>
/// The material to use for the floor <see href="https://docs.unity3d.com/ScriptReference/GameObject.html">GameObject</see> when created by the boundary system.
/// </summary>
public Material FloorMaterial => floorMaterial;
[PhysicsLayer]
[SerializeField]
[Tooltip("The physics layer to assign to the generated floor.")]
private int floorPhysicsLayer = 0;
/// <summary>
/// The physics layer to assign to the generated floor.
/// </summary>
public int FloorPhysicsLayer => floorPhysicsLayer;
[SerializeField]
[Tooltip("The dimensions of the floor, in meters.")]
private Vector2 floorScale = new Vector2(10f, 10f);
/// <summary>
/// The size at which to display the rectangular floor plane <see href="https://docs.unity3d.com/ScriptReference/GameObject.html">GameObject</see>.
/// </summary>
public Vector2 FloorScale => floorScale;
#endregion Floor settings
#region Play area settings
[SerializeField]
[Tooltip("Should the play area be displayed in the scene?")]
private bool showPlayArea = true;
/// <summary>
/// Should the boundary system display the play area?
/// </summary>
public bool ShowPlayArea => showPlayArea;
[SerializeField]
[Tooltip("The material to use when displaying the play area.")]
private Material playAreaMaterial = null;
/// <summary>
/// The material to use for the rectangular play area <see href="https://docs.unity3d.com/ScriptReference/GameObject.html">GameObject</see>.
/// </summary>
public Material PlayAreaMaterial => playAreaMaterial;
[PhysicsLayer]
[SerializeField]
[Tooltip("The physics layer to assign to the generated play area.")]
private int playAreaPhysicsLayer = 2;
/// <summary>
/// The physics layer to assign to the generated play area.
/// </summary>
public int PlayAreaPhysicsLayer => playAreaPhysicsLayer;
#endregion Play area settings
#region Tracked area settings
[SerializeField]
[Tooltip("Should the tracked area be displayed in the scene?")]
private bool showTrackedArea = true;
/// <summary>
/// Should the boundary system display the tracked area?
/// </summary>
public bool ShowTrackedArea => showTrackedArea;
[SerializeField]
[Tooltip("The material to use when displaying the tracked area.")]
private Material trackedAreaMaterial = null;
/// <summary>
/// The material to use for the boundary geometry <see href="https://docs.unity3d.com/ScriptReference/GameObject.html">GameObject</see>.
/// </summary>
public Material TrackedAreaMaterial => trackedAreaMaterial;
[PhysicsLayer]
[SerializeField]
[Tooltip("The physics layer to assign to the generated tracked area.")]
private int trackedAreaPhysicsLayer = 2;
/// <summary>
/// The physics layer to assign to the generated tracked area.
/// </summary>
public int TrackedAreaPhysicsLayer => trackedAreaPhysicsLayer;
#endregion Tracked area settings
#region Boundary wall settings
[SerializeField]
[Tooltip("Should the boundary walls be displayed in the scene?")]
private bool showBoundaryWalls = false;
/// <summary>
/// Should the boundary system display the boundary geometry walls?
/// </summary>
public bool ShowBoundaryWalls => showBoundaryWalls;
[SerializeField]
[Tooltip("The material to use when displaying the boundary walls.")]
private Material boundaryWallMaterial = null;
/// <summary>
/// The material to use for displaying the boundary geometry walls.
/// </summary>
public Material BoundaryWallMaterial => boundaryWallMaterial;
[PhysicsLayer]
[SerializeField]
[Tooltip("The physics layer to assign to the generated boundary walls.")]
private int boundaryWallsPhysicsLayer = 2;
/// <summary>
/// The physics layer to assign to the generated boundary walls.
/// </summary>
public int BoundaryWallsPhysicsLayer => boundaryWallsPhysicsLayer;
#endregion Boundary wall settings
#region Boundary ceiling settings
[SerializeField]
[Tooltip("Should the boundary ceiling be displayed in the scene?")]
private bool showBoundaryCeiling = false;
/// <summary>
/// Should the boundary system display the boundary ceiling?
/// </summary>
public bool ShowBoundaryCeiling => showBoundaryCeiling;
[SerializeField]
[Tooltip("The material to use when displaying the boundary ceiling.")]
private Material boundaryCeilingMaterial = null;
/// <summary>
/// The material to use for displaying the boundary ceiling.
/// </summary>
public Material BoundaryCeilingMaterial => boundaryCeilingMaterial;
[PhysicsLayer]
[SerializeField]
[Tooltip("The physics layer to assign to the generated boundary ceiling.")]
private int ceilingPhysicsLayer = 2;
/// <summary>
/// The physics layer to assign to the generated boundary ceiling.
/// </summary>
public int CeilingPhysicsLayer => ceilingPhysicsLayer;
#endregion Boundary ceiling settings
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9fd6338e77774badb73a2b1320b41caf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
namespace Microsoft.MixedReality.Toolkit.CameraSystem
{
/// <summary>
/// Base class used to derive custom camera settings profiles.
/// </summary>
[Serializable]
public class BaseCameraSettingsProfile : BaseMixedRealityProfile
{
// This class is intentionally blank. It exists for future expansion of common functionality.
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5d76cd22dd005ac438d4c183c7b85a54
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Copyright(c) 2019 Takahiro Miyaura
// Licensed under the MIT License.
namespace Microsoft.MixedReality.Toolkit.CameraSystem
{
/// <summary>
/// The type of displays on which an application may run.
/// </summary>
public enum DisplayType
{
/// <summary>
/// The display is opaque. Devices on the digital reality (ex: VR) side of the Mixed Reality
/// spectrum generally have opaque displays.
/// </summary>
Opaque = 0,
/// <summary>
/// The display is transparent. Devices on the physical reality (ex: Microsoft HoloLens) side
/// of the Mixed Reality spectrum generally have transparent displays.
/// </summary>
Transparent
}
}

View File

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

View File

@ -0,0 +1,140 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.CameraSystem;
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
using UnityEngine.Serialization;
namespace Microsoft.MixedReality.Toolkit
{
/// <summary>
/// This Scriptable Object tells you if your head mounted display (HMD)
/// is a transparent device or an occluded device.
/// Based on those values, you can customize your camera and quality settings.
/// </summary>
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Camera Profile", fileName = "MixedRealityCameraProfile", order = (int)CreateProfileMenuItemIndices.Camera)]
[MixedRealityServiceProfile(typeof(IMixedRealityCameraSystem))]
[HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/configuration/mixed-reality-configuration-guide#camera")]
public class MixedRealityCameraProfile : BaseMixedRealityProfile
{
[SerializeField]
[Tooltip("Configuration objects describing the registered settings providers.")]
private MixedRealityCameraSettingsConfiguration[] settingsConfigurations = new MixedRealityCameraSettingsConfiguration[0];
/// <summary>
/// Configuration objects describing the registered settings providers.
/// </summary>
public MixedRealityCameraSettingsConfiguration[] SettingsConfigurations
{
get { return settingsConfigurations; }
internal set { settingsConfigurations = value; }
}
[SerializeField]
[Tooltip("Near clipping plane distance for an opaque display.")]
private float nearClipPlaneOpaqueDisplay = 0.1f;
/// <summary>
/// Near clipping plane distance for an opaque display.
/// </summary>
public float NearClipPlaneOpaqueDisplay => nearClipPlaneOpaqueDisplay;
[SerializeField]
[Tooltip("Far clipping plane distance for an opaque display.")]
private float farClipPlaneOpaqueDisplay = 1000f;
/// <summary>
/// Far clipping plane distance for an opaque display.
/// </summary>
public float FarClipPlaneOpaqueDisplay => farClipPlaneOpaqueDisplay;
[SerializeField]
[Tooltip("Flags describing how to clear the camera for an opaque display.")]
private CameraClearFlags cameraClearFlagsOpaqueDisplay = CameraClearFlags.Skybox;
/// <summary>
/// Flags describing how to clear the camera for an opaque display.
/// </summary>
public CameraClearFlags CameraClearFlagsOpaqueDisplay => cameraClearFlagsOpaqueDisplay;
[SerializeField]
[Tooltip("Background color for an opaque display.")]
private Color backgroundColorOpaqueDisplay = Color.black;
/// <summary>
/// Background color for an opaque display.
/// </summary>
public Color BackgroundColorOpaqueDisplay => backgroundColorOpaqueDisplay;
[SerializeField]
[Tooltip("Quality level for an opaque display.")]
private int opaqueQualityLevel = 0;
/// <summary>
/// Quality level for an opaque display.
/// </summary>
public int OpaqueQualityLevel => opaqueQualityLevel;
[SerializeField]
[Tooltip("Near clipping plane distance for a transparent display.")]
private float nearClipPlaneTransparentDisplay = 0.85f;
/// <summary>
/// Near clipping plane distance for a transparent display.
/// </summary>
public float NearClipPlaneTransparentDisplay => nearClipPlaneTransparentDisplay;
[SerializeField]
[Tooltip("Far clipping plane distance for a transparent display.")]
private float farClipPlaneTransparentDisplay = 50f;
/// <summary>
/// Far clipping plane distance for a transparent display.
/// </summary>
public float FarClipPlaneTransparentDisplay => farClipPlaneTransparentDisplay;
[SerializeField]
[Tooltip("Flags describing how to clear the camera for a transparent display.")]
private CameraClearFlags cameraClearFlagsTransparentDisplay = CameraClearFlags.SolidColor;
/// <summary>
/// Flags describing how to clear the camera for a transparent display.
/// </summary>
public CameraClearFlags CameraClearFlagsTransparentDisplay => cameraClearFlagsTransparentDisplay;
[SerializeField]
[Tooltip("Background color for a transparent display.")]
private Color backgroundColorTransparentDisplay = Color.clear;
/// <summary>
/// Background color for a transparent display.
/// </summary>
public Color BackgroundColorTransparentDisplay => backgroundColorTransparentDisplay;
[SerializeField]
[Tooltip("Quality level for a transparent display.")]
[FormerlySerializedAs("holoLensQualityLevel")]
private int transparentQualityLevel = 0;
/// <summary>
/// Quality level for a transparent display.
/// </summary>
public int TransparentQualityLevel => transparentQualityLevel;
#region Obsolete properties
/// <summary>
/// Quality level for a HoloLens device.
/// </summary>
/// <remarks>
/// HoloLensQualityLevel is obsolete and will be removed in a future Mixed Reality Toolkit release. Please use TransparentQualityLevel.
/// </remarks>
[Obsolete("HoloLensQualityLevel is obsolete and will be removed in a future Mixed Reality Toolkit release. Please use TransparentQualityLevel.")]
public int HoloLensQualityLevel => transparentQualityLevel;
#endregion Obsolete properties
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4a1c93114e9437cb75d8b3ee4e0e1ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,79 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.CameraSystem
{
/// <summary>
/// Defines the configuration for a camera settings provider.
/// </summary>
[Serializable]
public struct MixedRealityCameraSettingsConfiguration : IMixedRealityServiceConfiguration
{
[SerializeField]
[Tooltip("The concrete type of the camera settings provider.")]
[Implements(typeof(IMixedRealityCameraSettingsProvider), TypeGrouping.ByNamespaceFlat)]
private SystemType componentType;
/// <inheritdoc />
public SystemType ComponentType => componentType;
[SerializeField]
[Tooltip("The name of the camera settings provider.")]
private string componentName;
/// <inheritdoc />
public string ComponentName => componentName;
[SerializeField]
[Tooltip("The camera settings provider priority.")]
private uint priority;
/// <inheritdoc />
public uint Priority => priority;
[SerializeField]
[Tooltip("The platform(s) on which the camera settings provider is supported.")]
[EnumFlags]
private SupportedPlatforms runtimePlatform;
/// <inheritdoc />
public SupportedPlatforms RuntimePlatform => runtimePlatform;
[SerializeField]
private BaseCameraSettingsProfile settingsProfile;
/// <inheritdoc />
public BaseMixedRealityProfile Profile => settingsProfile;
/// <summary>
/// Camera settings specific configuration profile.
/// </summary>
public BaseCameraSettingsProfile SettingsProfile => settingsProfile;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="componentType">The <see cref="Microsoft.MixedReality.Toolkit.Utilities.SystemType"/> of the provider.</param>
/// <param name="componentName">The friendly name of the provider.</param>
/// <param name="priority">The load priority of the provider.</param>
/// <param name="runtimePlatform">The runtime platform(s) supported by the provider.</param>
/// <param name="settingsProfile">The configuration profile for the provider.</param>
public MixedRealityCameraSettingsConfiguration(
SystemType componentType,
string componentName,
uint priority,
SupportedPlatforms runtimePlatform,
BaseCameraSettingsProfile configurationProfile)
{
this.componentType = componentType;
this.componentName = componentName;
this.priority = priority;
this.runtimePlatform = runtimePlatform;
this.settingsProfile = configurationProfile;
}
}
}

View File

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

View File

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

View File

@ -0,0 +1,461 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System.Collections.Generic;
using Unity.Profiling;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Defines the interactions and data that an articulated hand can provide.
/// </summary>
public class ArticulatedHandDefinition : BaseInputSourceDefinition
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="source">The input source backing this definition instance. Used for raising events.</param>
/// <param name="handedness">The handedness that this definition instance represents.</param>
public ArticulatedHandDefinition(IMixedRealityInputSource source, Handedness handedness) : base(handedness)
{
InputSource = source;
}
/// <summary>
/// The input source backing this definition instance.
/// </summary>
protected IMixedRealityInputSource InputSource { get; }
private readonly float cursorBeamBackwardTolerance = 0.5f;
private readonly float cursorBeamUpTolerance = 0.8f;
private IDictionary<TrackedHandJoint, MixedRealityPose> unityJointPoseDictionary = new Dictionary<TrackedHandJoint, MixedRealityPose>();
private MixedRealityPose[] unityJointPoses = null;
private MixedRealityPose currentIndexPose = MixedRealityPose.ZeroIdentity;
private Vector3 currentPalmNormal = Vector3.zero;
private const int PalmIndex = (int)TrackedHandJoint.Palm;
private const int ThumbTipIndex = (int)TrackedHandJoint.ThumbTip;
private const int IndexKnuckleIndex = (int)TrackedHandJoint.IndexKnuckle;
private const int IndexTipIndex = (int)TrackedHandJoint.IndexTip;
// Minimum distance between the index and the thumb tip required to enter a pinch
private const float MinimumPinchDistance = 0.015f;
// Maximum distance between the index and thumb tip required to exit the pinch gesture
private const float MaximumPinchDistance = 0.1f;
// Default enterPinchDistance value
private float enterPinchDistance = 0.02f;
/// <summary>
/// The distance between the index finger tip and the thumb tip required to enter the pinch/air tap selection gesture.
/// The pinch gesture enter will be registered for all values less than the EnterPinchDistance. The default EnterPinchDistance value is 0.02 and must be between 0.015 and 0.1.
/// </summary>
public float EnterPinchDistance
{
get => enterPinchDistance;
set
{
if (value >= MinimumPinchDistance && value <= MaximumPinchDistance)
{
enterPinchDistance = value;
}
else
{
Debug.LogError($"EnterPinchDistance must be between {MinimumPinchDistance} and {MaximumPinchDistance}.");
}
}
}
// Default exitPinchDistance value
private float exitPinchDistance = 0.05f;
/// <summary>
/// The distance between the index finger tip and the thumb tip required to exit the pinch/air tap gesture.
/// The pinch gesture exit will be registered for all values greater than the ExitPinchDistance. The default ExitPinchDistance value is 0.05 and must be between 0.015 and 0.1.
/// </summary>
public float ExitPinchDistance
{
get => exitPinchDistance;
set
{
if (value >= MinimumPinchDistance && value <= MaximumPinchDistance)
{
exitPinchDistance = value;
}
else
{
Debug.LogError($"ExitPinchDistance must be between {MinimumPinchDistance} and {MaximumPinchDistance}.");
}
}
}
/// <summary>
/// The articulated hands default interactions.
/// </summary>
/// <remarks>A single interaction mapping works for both left and right articulated hands.</remarks>
[System.Obsolete("Call GetDefaultMappings(Handedness) instead.")]
public MixedRealityInteractionMapping[] DefaultInteractions
{
get
{
MixedRealityInteractionMapping[] defaultInteractions = new MixedRealityInteractionMapping[DefaultMappings.Length];
for (int i = 0; i < DefaultMappings.Length; i++)
{
defaultInteractions[i] = new MixedRealityInteractionMapping((uint)i, DefaultMappings[i]);
}
return defaultInteractions;
}
}
/// <summary>
/// The articulated hands default interactions.
/// </summary>
/// <remarks>A single interaction mapping works for both left and right articulated hands.</remarks>
protected override MixedRealityInputActionMapping[] DefaultMappings => new[]
{
new MixedRealityInputActionMapping("Spatial Pointer", AxisType.SixDof, DeviceInputType.SpatialPointer),
new MixedRealityInputActionMapping("Spatial Grip", AxisType.SixDof, DeviceInputType.SpatialGrip),
new MixedRealityInputActionMapping("Select", AxisType.Digital, DeviceInputType.Select),
new MixedRealityInputActionMapping("Grab", AxisType.SingleAxis, DeviceInputType.GripPress),
new MixedRealityInputActionMapping("Index Finger Pose", AxisType.SixDof, DeviceInputType.IndexFinger),
new MixedRealityInputActionMapping("Teleport Pose", AxisType.DualAxis, DeviceInputType.ThumbStick),
};
// Internal calculation of what the HandRay should be
// Useful as a fallback for hand ray data
protected virtual IHandRay HandRay { get; } = new HandRay();
/// <summary>
/// Calculates whether the current pose allows for pointing/distant interactions.
/// Equivalent to the HandRay's ShouldShowRay implementation <see cref="HandRay.ShouldShowRay"/>
/// </summary>
public bool IsInPointingPose
{
get
{
if (unityJointPoses != null)
{
if (cursorBeamBackwardTolerance >= 0
&& CameraCache.Main != null
&& Vector3.Dot(currentPalmNormal.normalized, -CameraCache.Main.transform.forward) > cursorBeamBackwardTolerance)
{
return false;
}
if (cursorBeamUpTolerance >= 0
&& Vector3.Dot(currentPalmNormal, Vector3.up) > cursorBeamUpTolerance)
{
return false;
}
}
return !IsInTeleportPose;
}
}
/// <summary>
/// Calculates whether the current pose is the one to start a teleport action
/// </summary>
protected bool IsInTeleportPose
{
get
{
if (unityJointPoses == null)
{
return false;
}
Camera mainCamera = CameraCache.Main;
if (mainCamera == null)
{
return false;
}
Transform cameraTransform = mainCamera.transform;
// We check if the palm up is roughly in line with the camera up
return Vector3.Dot(currentPalmNormal, cameraTransform.up) > 0.6f
// Thumb must be extended, and middle must be grabbing
&& !isThumbGrabbing && isMiddleGrabbing;
}
}
/// <summary>
/// A bool tracking whether the hand definition is pinch or not
/// </summary>
private bool isPinching = false;
/// <summary>
/// Calculates whether the current the current joint pose is selecting (air tap gesture).
/// </summary>
public bool IsPinching
{
get
{
if (unityJointPoses != null)
{
float distance = Vector3.Distance(unityJointPoses[ThumbTipIndex].Position, currentIndexPose.Position);
if (isPinching && distance > ExitPinchDistance)
{
isPinching = false;
}
else if (!isPinching && distance < EnterPinchDistance)
{
isPinching = true;
}
}
else
{
isPinching = false;
}
return isPinching;
}
}
public bool IsGrabbing => isIndexGrabbing && isMiddleGrabbing;
private bool isIndexGrabbing;
private bool isMiddleGrabbing;
private bool isThumbGrabbing;
// Velocity internal states
private float deltaTimeStart;
private const int VelocityUpdateInterval = 6;
private int frameOn = 0;
private readonly Vector3[] velocityPositionsCache = new Vector3[VelocityUpdateInterval];
private readonly Vector3[] velocityNormalsCache = new Vector3[VelocityUpdateInterval];
private Vector3 velocityPositionsSum = Vector3.zero;
private Vector3 velocityNormalsSum = Vector3.zero;
public Vector3 AngularVelocity { get; protected set; }
public Vector3 Velocity { get; protected set; }
private static readonly ProfilerMarker UpdateHandJointsPerfMarker = new ProfilerMarker("[MRTK] ArticulatedHandDefinition.UpdateHandJoints");
#region Hand Definition Update functions
/// <summary>
/// Updates the current hand joints with new data.
/// </summary>
/// <param name="jointPoses">The new joint poses.</param>
public void UpdateHandJoints(Dictionary<TrackedHandJoint, MixedRealityPose> jointPoses)
{
using (UpdateHandJointsPerfMarker.Auto())
{
unityJointPoseDictionary = jointPoses;
_ = unityJointPoseDictionary.TryGetValue(TrackedHandJoint.IndexTip, out currentIndexPose);
if (unityJointPoseDictionary.TryGetValue(TrackedHandJoint.Palm, out MixedRealityPose palmPose))
{
currentPalmNormal = palmPose.Rotation * Vector3.down;
}
if (unityJointPoses == null)
{
unityJointPoses = new MixedRealityPose[ArticulatedHandPose.JointCount];
}
for (int i = 1; i < ArticulatedHandPose.JointCount; i++)
{
unityJointPoseDictionary.TryGetValue((TrackedHandJoint)i, out unityJointPoses[i]);
}
CoreServices.InputSystem?.RaiseHandJointsUpdated(InputSource, Handedness, unityJointPoseDictionary);
}
}
/// <summary>
/// Updates the current hand joints with new data.
/// </summary>
/// <param name="jointPoses">The new joint poses.</param>
public void UpdateHandJoints(MixedRealityPose[] jointPoses)
{
using (UpdateHandJointsPerfMarker.Auto())
{
unityJointPoses = jointPoses;
if (unityJointPoses == null)
{
return;
}
currentIndexPose = unityJointPoses[IndexTipIndex];
currentPalmNormal = unityJointPoses[PalmIndex].Rotation * Vector3.down;
for (int i = 1; i < ArticulatedHandPose.JointCount; i++)
{
unityJointPoseDictionary[(TrackedHandJoint)i] = unityJointPoses[i];
}
CoreServices.InputSystem?.RaiseHandJointsUpdated(InputSource, Handedness, unityJointPoseDictionary);
}
}
private static readonly ProfilerMarker UpdateCurrentIndexPosePerfMarker = new ProfilerMarker("[MRTK] ArticulatedHandDefinition.UpdateCurrentIndexPose");
/// <summary>
/// Updates the MixedRealityInteractionMapping with the latest index pose and fires a corresponding pose event.
/// </summary>
/// <param name="interactionMapping">The index finger's interaction mapping.</param>
public void UpdateCurrentIndexPose(MixedRealityInteractionMapping interactionMapping)
{
using (UpdateCurrentIndexPosePerfMarker.Auto())
{
if (unityJointPoses != null)
{
// Update the interaction data source
interactionMapping.PoseData = currentIndexPose;
// If our value changed raise it
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
CoreServices.InputSystem?.RaisePoseInputChanged(InputSource, Handedness, interactionMapping.MixedRealityInputAction, currentIndexPose);
}
}
}
}
// Used to track the input that was last raised
private bool previousReadyToTeleport = false;
private IMixedRealityTeleportPointer teleportPointer;
private static readonly ProfilerMarker UpdateCurrentTeleportPosePerfMarker = new ProfilerMarker("[MRTK] ArticulatedHandDefinition.UpdateCurrentTeleportPose");
/// <summary>
/// Updates the MixedRealityInteractionMapping with the latest teleport pose status and fires an event when appropriate
/// </summary>
/// <param name="interactionMapping">The teleport action's interaction mapping.</param>
public void UpdateCurrentTeleportPose(MixedRealityInteractionMapping interactionMapping)
{
using (UpdateCurrentTeleportPosePerfMarker.Auto())
{
// Check if we're focus locked or near something interactive to avoid teleporting unintentionally.
bool anyPointersLockedWithHand = false;
for (int i = 0; i < InputSource?.Pointers?.Length; i++)
{
IMixedRealityPointer mixedRealityPointer = InputSource.Pointers[i];
if (mixedRealityPointer.IsNull()) continue;
if (mixedRealityPointer is IMixedRealityNearPointer nearPointer)
{
anyPointersLockedWithHand |= nearPointer.IsNearObject;
}
anyPointersLockedWithHand |= mixedRealityPointer.IsFocusLocked;
// If official teleport mode and we have a teleport pointer registered, we get the input action to trigger it.
if (teleportPointer == null && mixedRealityPointer is IMixedRealityTeleportPointer pointer)
{
teleportPointer = pointer;
}
}
// We close middle finger to signal spider-man gesture, and as being ready for teleport
isIndexGrabbing = HandPoseUtils.IsIndexGrabbing(Handedness);
isMiddleGrabbing = HandPoseUtils.IsMiddleGrabbing(Handedness);
isThumbGrabbing = HandPoseUtils.IsThumbGrabbing(Handedness);
bool isReadyForTeleport = !anyPointersLockedWithHand && IsInTeleportPose;
// Tracks the input vector that should be sent out based on the gesture that is made
Vector2 stickInput = (isReadyForTeleport && !isIndexGrabbing) ? Vector2.up : Vector2.zero;
// The teleport event needs to be canceled if we have not completed the teleport motion and we were previously ready to teleport, but for some reason we
// are no longer doing the ready to teleport gesture
bool teleportCanceled = previousReadyToTeleport && !isReadyForTeleport && !isIndexGrabbing;
if (teleportCanceled && teleportPointer != null)
{
CoreServices.TeleportSystem?.RaiseTeleportCanceled(teleportPointer, null);
previousReadyToTeleport = isReadyForTeleport;
return;
}
// Update the interaction data source
interactionMapping.Vector2Data = stickInput;
// If our value changed raise it
if (interactionMapping.Changed)
{
CoreServices.InputSystem?.RaisePositionInputChanged(InputSource, Handedness, interactionMapping.MixedRealityInputAction, stickInput);
}
previousReadyToTeleport = isReadyForTeleport;
}
}
/// <summary>
/// Updates the MixedRealityInteractionMapping with the latest pointer pose status and fires a corresponding pose event.
/// </summary>
/// <param name="interactionMapping">The pointer pose's interaction mapping.</param>
public void UpdatePointerPose(MixedRealityInteractionMapping interactionMapping)
{
if (unityJointPoses == null) return;
Vector3 rayPosition = unityJointPoses[IndexKnuckleIndex].Position;
HandRay.Update(rayPosition, currentPalmNormal, CameraCache.Main.transform, Handedness);
Ray ray = HandRay.Ray;
// Update the interaction data source
interactionMapping.PoseData = new MixedRealityPose(ray.origin, Quaternion.LookRotation(ray.direction));
// If our value changed raise it
if (interactionMapping.Changed)
{
// Raise input system event if it's enabled
CoreServices.InputSystem?.RaisePoseInputChanged(InputSource, Handedness, interactionMapping.MixedRealityInputAction, interactionMapping.PoseData);
}
}
/// <summary>
/// Updates the hand definition with its velocity
/// </summary>
public void UpdateVelocity()
{
if (unityJointPoses != null)
{
Vector3 palmPosition = unityJointPoses[PalmIndex].Position;
if (frameOn < VelocityUpdateInterval)
{
velocityPositionsCache[frameOn] = palmPosition;
velocityPositionsSum += velocityPositionsCache[frameOn];
velocityNormalsCache[frameOn] = currentPalmNormal;
velocityNormalsSum += velocityNormalsCache[frameOn];
}
else
{
int frameIndex = frameOn % VelocityUpdateInterval;
float deltaTime = Time.unscaledTime - deltaTimeStart;
Vector3 newPositionsSum = velocityPositionsSum - velocityPositionsCache[frameIndex] + palmPosition;
Vector3 newNormalsSum = velocityNormalsSum - velocityNormalsCache[frameIndex] + currentPalmNormal;
Velocity = (newPositionsSum - velocityPositionsSum) / deltaTime / VelocityUpdateInterval;
Quaternion rotation = Quaternion.FromToRotation(velocityNormalsSum / VelocityUpdateInterval, newNormalsSum / VelocityUpdateInterval);
Vector3 rotationRate = rotation.eulerAngles * Mathf.Deg2Rad;
AngularVelocity = rotationRate / deltaTime;
velocityPositionsCache[frameIndex] = palmPosition;
velocityNormalsCache[frameIndex] = currentPalmNormal;
velocityPositionsSum = newPositionsSum;
velocityNormalsSum = newNormalsSum;
}
deltaTimeStart = Time.unscaledTime;
frameOn++;
}
}
#endregion
}
}

View File

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

View File

@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System.Collections.Generic;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Defines the base interactions and data that an controller can provide.
/// </summary>
public abstract class BaseInputSourceDefinition : IMixedRealityInputSourceDefinition
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="handedness">The handedness that this definition instance represents.</param>
public BaseInputSourceDefinition(Handedness handedness)
{
Handedness = handedness;
}
/// <summary>
/// The <see cref="Handedness"/> (ex: Left, Right, None) of this controller.
/// </summary>
public Handedness Handedness { get; }
/// <summary>
/// The collection of interactions supported by a left-handed instance of this controller.
/// </summary>
/// <remarks>Optional. Override DefaultInteractions if both handed controllers have identical interactions.</remarks>
protected virtual MixedRealityInputActionMapping[] DefaultLeftHandedMappings => DefaultMappings;
/// <summary>
/// The collection of interactions supported by a right-handed instance of this controller.
/// </summary>
/// <remarks>Optional. Override DefaultInteractions if both handed controllers have identical interactions.</remarks>
protected virtual MixedRealityInputActionMapping[] DefaultRightHandedMappings => DefaultMappings;
/// <summary>
/// The collection of interactions supported by this controller.
/// </summary>
/// <remarks>Optional. Override the specifically-handed properties if each controller has different interactions.</remarks>
protected virtual MixedRealityInputActionMapping[] DefaultMappings => null;
/// <inheritdoc />
public IReadOnlyList<MixedRealityInputActionMapping> GetDefaultMappings(Handedness handedness)
{
switch (handedness)
{
case Handedness.Left:
return DefaultLeftHandedMappings;
case Handedness.Right:
return DefaultRightHandedMappings;
default:
return DefaultMappings;
}
}
}
}

View File

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

View File

@ -0,0 +1,324 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#if UNITY_EDITOR
using Microsoft.MixedReality.Toolkit.Utilities;
using Microsoft.MixedReality.Toolkit.Utilities.Editor;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
#endif
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Helper utility to manage all the required Axis configuration for platforms, where required
/// </summary>
public static class ControllerMappingLibrary
{
#region Constants
/// <summary>
/// Axis for movement along the up (gravity) vector.
/// </summary>
public const string UP_DOWN = "UpDown";
/// <summary>
/// Mouse: Position Horizontal Movement<para/>
/// HTC Vive Controller: Left Controller Trackpad (2) Horizontal Movement<para/>
/// Oculus Touch Controller: Axis2D.PrimaryThumbstick Horizontal Movement<para/>
/// Valve Knuckles Controller: Left Controller Trackpad Horizontal Movement<para/>
/// Windows Mixed Reality Motion Controller: Left Thumbstick Horizontal Movement<para/>
/// Xbox Controller: Left Thumbstick Horizontal Movement<para/>
/// </summary>
public const string AXIS_1 = "AXIS_1";
/// <summary>
/// Mouse: Position Vertical Movement<para/>
/// HTC Vive Controller: Left Controller Trackpad (2) Vertical Movement<para/>
/// Oculus Touch Controller: Axis2D.PrimaryThumbstick Vertical Movement<para/>
/// Valve Knuckles Controller: Left Controller Trackpad Vertical Movement<para/>
/// Windows Mixed Reality Motion Controller: Left Thumbstick Vertical Movement<para/>
/// Xbox Controller: Left Thumbstick Vertical Movement<para/>
/// </summary>
public const string AXIS_2 = "AXIS_2";
/// <summary>
/// Mouse: Scroll<para/>
/// Xbox Controller: Shared Trigger<para/>
/// </summary>
public const string AXIS_3 = "AXIS_3";
/// <summary>
/// HTC Vive Controller: Right Controller Trackpad (2) Horizontal Movement<para/>
/// Oculus Touch Controller: Axis2D.SecondaryThumbstick Horizontal Movement<para/>
/// Valve Knuckles Controller: Right Controller Trackpad Horizontal Movement<para/>
/// Windows Mixed Reality Motion Controller: Right Thumbstick Horizontal Movement<para/>
/// Xbox Controller: Right Thumbstick Vertical Movement<para/>
/// </summary>
public const string AXIS_4 = "AXIS_4";
/// <summary>
/// HTC Vive Controller: Right Controller Trackpad (2) Vertical Movement<para/>
/// Oculus Touch Controller: Axis2D.SecondaryThumbstick Vertical Movement<para/>
/// Valve Knuckles Controller: Right Controller Trackpad Vertical Movement<para/>
/// Windows Mixed Reality Motion Controller: Right Thumbstick Vertical Movement<para/>
/// Xbox Controller: Right Thumbstick Vertical Movement<para/>
/// </summary>
public const string AXIS_5 = "AXIS_5";
/// <summary>
/// None
/// </summary>
public const string AXIS_6 = "AXIS_6";
/// <summary>
/// Xbox Controller: D-Pad Horizontal<para/>
/// </summary>
public const string AXIS_7 = "AXIS_7";
/// <summary>
/// Xbox Controller: D-Pad Vertical<para/>
/// </summary>
public const string AXIS_8 = "AXIS_8";
/// <summary>
/// HTC Vive Controller: Left Controller Trigger (7) Squeeze<para/>
/// Oculus Touch Controller: Axis1D.PrimaryIndexTrigger Squeeze<para/>
/// Valve Knuckles Controller: Left Controller Trigger Squeeze<para/>
/// Windows Mixed Reality Motion Controller: Left Trigger Squeeze<para/>
/// </summary>
public const string AXIS_9 = "AXIS_9";
/// <summary>
/// HTC Vive Controller: Right Controller Trigger (7) Squeeze<para/>
/// Oculus Touch Controller: Axis1D.SecondaryIndexTrigger Movement Squeeze<para/>
/// Valve Knuckles Controller: Right Controller Trigger Squeeze<para/>
/// Windows Mixed Reality Motion Controller: Right Trigger Squeeze<para/>
/// </summary>
public const string AXIS_10 = "AXIS_10";
/// <summary>
/// HTC Vive Controller: Left Controller Grip Button (8) Squeeze<para/>
/// Oculus Touch Controller: Axis1D.PrimaryHandTrigger Squeeze<para/>
/// Valve Knuckles Controller: Left Controller Grip Average Squeeze<para/>
/// Windows Mixed Reality Motion Controller: Left Grip Squeeze<para/>
/// </summary>
public const string AXIS_11 = "AXIS_11";
/// <summary>
/// HTC Vive Controller: Right Controller Grip Button (8) Squeeze<para/>
/// Oculus Touch Controller: Axis1D.SecondaryHandTrigger Squeeze<para/>
/// Valve Knuckles Controller: Right Controller Grip Average Squeeze<para/>
/// Windows Mixed Reality Motion Controller: Right Grip Squeeze<para/>
/// </summary>
public const string AXIS_12 = "AXIS_12";
/// <summary>
/// Oculus Touch Controller: Axis1D.PrimaryIndexTrigger Near Touch<para/>
/// </summary>
public const string AXIS_13 = "AXIS_13";
/// <summary>
/// Oculus Touch Controller: Axis1D.SecondaryIndexTrigger Near Touch<para/>
/// </summary>
public const string AXIS_14 = "AXIS_14";
/// <summary>
/// Oculus Touch Controller: Touch.PrimaryThumbRest Near Touch<para/>
/// </summary>
public const string AXIS_15 = "AXIS_15";
/// <summary>
/// Oculus Touch Controller: Button.SecondaryThumbstick Near Touch<para/>
/// </summary>
public const string AXIS_16 = "AXIS_16";
/// <summary>
/// Windows Mixed Reality Motion Controller: Left Touchpad Horizontal Movement<para/>
/// </summary>
public const string AXIS_17 = "AXIS_17";
/// <summary>
/// Windows Mixed Reality Motion Controller: Left Touchpad Vertical Movement<para/>
/// </summary>
public const string AXIS_18 = "AXIS_18";
/// <summary>
/// Windows Mixed Reality Motion Controller: Right Touchpad Horizontal Movement<para/>
/// </summary>
public const string AXIS_19 = "AXIS_19";
/// <summary>
/// Windows Mixed Reality Motion Controller: Right Touchpad Vertical Movement<para/>
/// Valve Knuckles Controller: Left Controller Index Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_20 = "AXIS_20";
/// <summary>
/// Valve Knuckles Controller: Right Controller Index Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_21 = "AXIS_21";
/// <summary>
/// Valve Knuckles Controller: Left Controller Middle Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_22 = "AXIS_22";
/// <summary>
/// Valve Knuckles Controller: Right Controller Middle Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_23 = "AXIS_23";
/// <summary>
/// Valve Knuckles Controller: Left Controller Ring Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_24 = "AXIS_24";
/// <summary>
/// Valve Knuckles Controller: Right Controller Ring Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_25 = "AXIS_25";
/// <summary>
/// Valve Knuckles Controller: Left Controller Pinky Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_26 = "AXIS_26";
/// <summary>
/// Valve Knuckles Controller: Right Controller Pinky Finger Cap Sensor<para/>
/// </summary>
public const string AXIS_27 = "AXIS_27";
public const string AXIS_28 = "AXIS_28";
#endregion Constants
#if UNITY_EDITOR
#region InputAxisConfig
// Default value for the dead zone. This should match the default used by Unity for the pre-created Horizontal and Vertical axes.
public const float defaultDeadZone = 0.19f;
/// <summary>
/// Get the InputManagerAxis data needed to configure the Input Mappings for a controller
/// </summary>
public static InputManagerAxis[] UnityInputManagerAxes => new[]
{
new InputManagerAxis { Name = AXIS_1, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 1 },
new InputManagerAxis { Name = AXIS_2, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 2 },
new InputManagerAxis { Name = AXIS_3, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 3 },
new InputManagerAxis { Name = AXIS_4, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 4 },
new InputManagerAxis { Name = AXIS_5, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 5 },
new InputManagerAxis { Name = AXIS_6, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 6 },
new InputManagerAxis { Name = AXIS_7, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 7 },
new InputManagerAxis { Name = AXIS_8, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 8 },
new InputManagerAxis { Name = AXIS_9, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 9 },
new InputManagerAxis { Name = AXIS_10, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 10 },
new InputManagerAxis { Name = AXIS_11, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 11 },
new InputManagerAxis { Name = AXIS_12, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 12 },
new InputManagerAxis { Name = AXIS_13, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 13 },
new InputManagerAxis { Name = AXIS_14, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 14 },
new InputManagerAxis { Name = AXIS_15, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 15 },
new InputManagerAxis { Name = AXIS_16, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 16 },
new InputManagerAxis { Name = AXIS_17, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 17 },
new InputManagerAxis { Name = AXIS_18, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 18 },
new InputManagerAxis { Name = AXIS_19, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 19 },
new InputManagerAxis { Name = AXIS_20, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 20 },
new InputManagerAxis { Name = AXIS_21, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 21 },
new InputManagerAxis { Name = AXIS_22, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 22 },
new InputManagerAxis { Name = AXIS_23, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 23 },
new InputManagerAxis { Name = AXIS_24, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 24 },
new InputManagerAxis { Name = AXIS_25, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 25 },
new InputManagerAxis { Name = AXIS_26, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 26 },
new InputManagerAxis { Name = AXIS_27, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 27 },
new InputManagerAxis { Name = AXIS_28, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 28 },
new InputManagerAxis { Name = UP_DOWN, Gravity = 3, Dead = 0.001f, Sensitivity = 3, Snap = true, Invert = false, Type = InputManagerAxisType.KeyOrMouseButton, PositiveButton = "e", NegativeButton = "q" },
new InputManagerAxis { Name = UP_DOWN, Dead = defaultDeadZone, Sensitivity = 1, Invert = false, Type = InputManagerAxisType.JoystickAxis, Axis = 3 },
};
#endregion InputAxisConfig
private static Dictionary<Tuple<Type, Handedness, string>, Texture2D> cachedTextures = new Dictionary<Tuple<Type, Handedness, string>, Texture2D>();
public static Texture2D GetControllerTexture(Type controllerType, Handedness handedness)
{
return GetControllerTextureCached(controllerType, handedness, "");
}
public static Texture2D GetControllerTextureScaled(Type controllerType, Handedness handedness)
{
return GetControllerTextureCached(controllerType, handedness, "_scaled");
}
private static Texture2D GetControllerTextureCached(Type controllerType, Handedness handedness, string suffix)
{
var key = new Tuple<Type, Handedness, string>(controllerType, handedness, suffix);
if (cachedTextures.TryGetValue(key, out Texture2D texture))
{
return texture;
}
texture = GetControllerTextureInternal(controllerType, handedness, suffix);
cachedTextures.Add(key, texture);
return texture;
}
private static Texture2D GetControllerTextureInternal(Type controllerType, Handedness handedness, string suffix)
{
if (controllerType != null)
{
var attr = MixedRealityControllerAttribute.Find(controllerType);
if (attr != null)
{
if (attr.TexturePath.Length > 0)
{
Texture2D texture = GetControllerTextureInternal(attr.TexturePath, handedness, suffix);
if (texture != null)
{
return texture;
}
}
}
}
return GetControllerTextureInternal("Textures/Generic_controller", Handedness.None, suffix);
}
private static Texture2D GetControllerTextureInternal(string relativeTexturePath, Handedness handedness, string suffix)
{
string handednessSuffix = string.Empty;
if (handedness == Handedness.Left)
{
handednessSuffix = "_left";
}
else if (handedness == Handedness.Right)
{
handednessSuffix = "_right";
}
string themeSuffix = EditorGUIUtility.isProSkin ? "_white" : "_black";
string textureName = $"{Path.GetFileName(relativeTexturePath)}{handednessSuffix}{themeSuffix}{suffix}";
string[] textureGuids = AssetDatabase.FindAssets(textureName);
string texturePath = string.Empty;
foreach (string guid in textureGuids)
{
string tempPath = AssetDatabase.GUIDToAssetPath(guid);
// Ensure the path we're looking at contains the exact file name we're looking for
if (tempPath.Contains(textureName + ".png"))
{
texturePath = tempPath;
break;
}
}
return (Texture2D)AssetDatabase.LoadAssetAtPath(texturePath, typeof(Texture2D));
}
#endif // UNITY_EDITOR
}
}

View File

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

View File

@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.MixedReality.Toolkit.Input
{
// TODO - Expand input list for additional controller types and have a filter defined by the controller
/// <summary>
/// The InputType defines the types of input exposed by a controller.
/// Denoting the available buttons / interactions that a controller supports.
/// </summary>
public enum DeviceInputType
{
None = 0,
Gaze,
Voice,
/// <summary>
/// 6-DoF pointer with position and rotation.
/// </summary>
SpatialPointer,
/// <summary>
/// 3-DoF pointer with only position.
/// </summary>
PointerPosition,
/// <summary>
/// 3-DoF pointer with only rotation.
/// </summary>
PointerRotation,
PointerClick,
ButtonPress,
ButtonTouch,
ButtonNearTouch,
Trigger,
TriggerTouch,
TriggerNearTouch,
// TriggerPress, in some cases, maps to the grab/grasp gesture.
TriggerPress,
/// <summary>
/// 6-DoF grip with position and rotation.
/// </summary>
SpatialGrip,
/// <summary>
/// 3-DoF grip with only position.
/// </summary>
GripPosition,
/// <summary>
/// 3-DoF grip with only rotation.
/// </summary>
GripRotation,
ThumbStick,
ThumbStickPress,
ThumbStickTouch,
ThumbStickNearTouch,
Touchpad,
TouchpadTouch,
TouchpadNearTouch,
TouchpadPress,
/// <summary>
/// Select, in some cases, maps to the pinch/airtap gesture.
/// </summary>
Select,
Start,
Menu,
Hand,
Thumb,
ThumbTouch,
ThumbNearTouch,
ThumbPress,
IndexFinger,
IndexFingerTouch,
IndexFingerNearTouch,
IndexFingerPress,
MiddleFinger,
MiddleFingerTouch,
MiddleFingerNearTouch,
MiddleFingerPress,
RingFinger,
RingFingerTouch,
RingFingerNearTouch,
RingFingerPress,
PinkyFinger,
PinkyFingerTouch,
PinkyFingerNearTouch,
PinkyFingerPress,
DirectionalPad,
Scroll,
PrimaryButtonPress,
PrimaryButtonTouch,
PrimaryButtonNearTouch,
SecondaryButtonPress,
SecondaryButtonTouch,
SecondaryButtonNearTouch,
Grip,
GripTouch,
GripNearTouch,
// GripPress, in some cases, maps to the grab/grasp gesture.
GripPress,
}
}

View File

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

View File

@ -0,0 +1,126 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
///
/// </summary>
public class GenericOpenVRControllerDefinition : BaseInputSourceDefinition
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="handedness">The handedness that this definition instance represents.</param>
public GenericOpenVRControllerDefinition(Handedness handedness) : base(handedness)
{ }
/// <inheritdoc />
protected override MixedRealityInputActionMapping[] DefaultLeftHandedMappings => new[]
{
// Controller Pose
new MixedRealityInputActionMapping("Spatial Pointer", AxisType.SixDof, DeviceInputType.SpatialPointer),
// HTC Vive Controller - Left Controller Trigger (7) Squeeze
// Oculus Touch Controller - Axis1D.PrimaryIndexTrigger Squeeze
// Valve Knuckles Controller - Left Controller Trigger Squeeze
// Windows Mixed Reality Controller - Left Trigger Squeeze
new MixedRealityInputActionMapping("Trigger Position", AxisType.SingleAxis, DeviceInputType.Trigger),
// HTC Vive Controller - Left Controller Trigger (7)
// Oculus Touch Controller - Axis1D.PrimaryIndexTrigger
// Valve Knuckles Controller - Left Controller Trigger
// Windows Mixed Reality Controller - Left Trigger Press (Select)
new MixedRealityInputActionMapping("Trigger Press (Select)", AxisType.Digital, DeviceInputType.Select),
// HTC Vive Controller - Left Controller Trigger (7)
// Oculus Touch Controller - Axis1D.PrimaryIndexTrigger
// Valve Knuckles Controller - Left Controller Trigger
new MixedRealityInputActionMapping("Trigger Touch", AxisType.Digital, DeviceInputType.TriggerTouch),
// HTC Vive Controller - Left Controller Grip Button (8)
// Oculus Touch Controller - Axis1D.PrimaryHandTrigger
// Valve Knuckles Controller - Left Controller Grip Average
// Windows Mixed Reality Controller - Left Grip Button Press
new MixedRealityInputActionMapping("Grip Trigger Position", AxisType.SingleAxis, DeviceInputType.Trigger),
// HTC Vive Controller - Left Controller Trackpad (2)
// Oculus Touch Controller - Axis2D.PrimaryThumbstick
// Valve Knuckles Controller - Left Controller Trackpad
// Windows Mixed Reality Controller - Left Thumbstick Position
new MixedRealityInputActionMapping("Trackpad-Thumbstick Position", AxisType.DualAxis, DeviceInputType.Touchpad),
// HTC Vive Controller - Left Controller Trackpad (2)
// Oculus Touch Controller - Button.PrimaryThumbstick
// Valve Knuckles Controller - Left Controller Trackpad
// Windows Mixed Reality Controller - Left Touchpad Touch
new MixedRealityInputActionMapping("Trackpad-Thumbstick Touch", AxisType.Digital, DeviceInputType.TouchpadTouch),
// HTC Vive Controller - Left Controller Trackpad (2)
// Oculus Touch Controller - Button.PrimaryThumbstick
// Valve Knuckles Controller - Left Controller Trackpad
// Windows Mixed Reality Controller - Left Thumbstick Press
new MixedRealityInputActionMapping("Trackpad-Thumbstick Press", AxisType.Digital, DeviceInputType.TouchpadPress),
// HTC Vive Controller - Left Controller Menu Button (1)
// Oculus Touch Controller - Button.Three Press
// Valve Knuckles Controller - Left Controller Inner Face Button
// Windows Mixed Reality Controller - Left Menu Button
new MixedRealityInputActionMapping("Unity Button Id 2", AxisType.Digital, DeviceInputType.ButtonPress),
// Oculus Touch Controller - Button.Four Press
// Valve Knuckles Controller - Left Controller Outer Face Button
new MixedRealityInputActionMapping("Unity Button Id 3", AxisType.Digital, DeviceInputType.ButtonPress),
new MixedRealityInputActionMapping("WMR Touchpad Touch", AxisType.Digital, DeviceInputType.TouchpadTouch),
new MixedRealityInputActionMapping("WMR Touchpad Position", AxisType.DualAxis, DeviceInputType.Touchpad),
new MixedRealityInputActionMapping("Spatial Grip", AxisType.SixDof, DeviceInputType.SpatialGrip),
};
/// <inheritdoc />
protected override MixedRealityInputActionMapping[] DefaultRightHandedMappings => new[]
{
// Controller Pose
new MixedRealityInputActionMapping("Spatial Pointer", AxisType.SixDof, DeviceInputType.SpatialPointer),
// HTC Vive Controller - Right Controller Trigger (7) Squeeze
// Oculus Touch Controller - Axis1D.SecondaryIndexTrigger Squeeze
// Valve Knuckles Controller - Right Controller Trigger Squeeze
// Windows Mixed Reality Controller - Right Trigger Squeeze
new MixedRealityInputActionMapping("Trigger Position", AxisType.SingleAxis, DeviceInputType.Trigger),
// HTC Vive Controller - Right Controller Trigger (7)
// Oculus Touch Controller - Axis1D.SecondaryIndexTrigger
// Valve Knuckles Controller - Right Controller Trigger
// Windows Mixed Reality Controller - Right Trigger Press (Select)
new MixedRealityInputActionMapping("Trigger Press (Select)", AxisType.Digital, DeviceInputType.Select),
// HTC Vive Controller - Right Controller Trigger (7)
// Oculus Touch Controller - Axis1D.SecondaryIndexTrigger
// Valve Knuckles Controller - Right Controller Trigger
new MixedRealityInputActionMapping("Trigger Touch", AxisType.Digital, DeviceInputType.TriggerTouch),
// HTC Vive Controller - Right Controller Grip Button (8)
// Oculus Touch Controller - Axis1D.SecondaryHandTrigger
// Valve Knuckles Controller - Right Controller Grip Average
// Windows Mixed Reality Controller - Right Grip Button Press
new MixedRealityInputActionMapping("Grip Trigger Position", AxisType.SingleAxis, DeviceInputType.Trigger),
// HTC Vive Controller - Right Controller Trackpad (2)
// Oculus Touch Controller - Axis2D.PrimaryThumbstick
// Valve Knuckles Controller - Right Controller Trackpad
// Windows Mixed Reality Controller - Right Thumbstick Position
new MixedRealityInputActionMapping("Trackpad-Thumbstick Position", AxisType.DualAxis, DeviceInputType.Touchpad),
// HTC Vive Controller - Right Controller Trackpad (2)
// Oculus Touch Controller - Button.SecondaryThumbstick
// Valve Knuckles Controller - Right Controller Trackpad
// Windows Mixed Reality Controller - Right Touchpad Touch
new MixedRealityInputActionMapping("Trackpad-Thumbstick Touch", AxisType.Digital, DeviceInputType.TouchpadTouch),
// HTC Vive Controller - Right Controller Trackpad (2)
// Oculus Touch Controller - Button.SecondaryThumbstick
// Valve Knuckles Controller - Right Controller Trackpad
// Windows Mixed Reality Controller - Right Thumbstick Press
new MixedRealityInputActionMapping("Trackpad-Thumbstick Press", AxisType.Digital, DeviceInputType.TouchpadPress),
// HTC Vive Controller - Right Controller Menu Button (1)
// Oculus Remote - Button.One Press
// Oculus Touch Controller - Button.One Press
// Valve Knuckles Controller - Right Controller Inner Face Button
// Windows Mixed Reality Controller - Right Menu Button
new MixedRealityInputActionMapping("Unity Button Id 0", AxisType.Digital, DeviceInputType.ButtonPress),
// Oculus Remote - Button.Two Press
// Oculus Touch Controller - Button.Two Press
// Valve Knuckles Controller - Right Controller Outer Face Button
new MixedRealityInputActionMapping("Unity Button Id 1", AxisType.Digital, DeviceInputType.ButtonPress),
new MixedRealityInputActionMapping("WMR Touchpad Touch", AxisType.Digital, DeviceInputType.TouchpadTouch),
new MixedRealityInputActionMapping("WMR Touchpad Position", AxisType.DualAxis, DeviceInputType.Touchpad),
new MixedRealityInputActionMapping("Spatial Grip", AxisType.SixDof, DeviceInputType.SpatialGrip),
};
}
}

View File

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

View File

@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// The GestureInputType defines the types of gestures exposed by a controller.
/// </summary>
public enum GestureInputType
{
None = 0,
Hold,
Navigation,
Manipulation,
Select
}
}

View File

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

View File

@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
namespace Microsoft.MixedReality.Toolkit.Input
{
public class HPMotionControllerDefinition : BaseInputSourceDefinition
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="handedness">The handedness that this definition instance represents.</param>
public HPMotionControllerDefinition(Handedness handedness) : base(handedness)
{
if ((handedness != Handedness.Left) &&
(handedness != Handedness.Right))
{
throw new System.ArgumentException($"Unsupported Handedness ({handedness}). The OculusTouchControllerDefinition supports Left and Right.");
}
}
/// <inheritdoc />
protected override MixedRealityInputActionMapping[] DefaultLeftHandedMappings => new[]
{
new MixedRealityInputActionMapping("Spatial Pointer", AxisType.SixDof, DeviceInputType.SpatialPointer),
new MixedRealityInputActionMapping("Spatial Grip", AxisType.SixDof, DeviceInputType.SpatialGrip),
new MixedRealityInputActionMapping("Grip Position", AxisType.SingleAxis, DeviceInputType.Grip),
new MixedRealityInputActionMapping("Grip Touch", AxisType.Digital, DeviceInputType.GripTouch),
new MixedRealityInputActionMapping("Grip Press", AxisType.SingleAxis, DeviceInputType.GripPress),
new MixedRealityInputActionMapping("Trigger Position", AxisType.SingleAxis, DeviceInputType.Trigger),
new MixedRealityInputActionMapping("Trigger Touch", AxisType.Digital, DeviceInputType.TriggerTouch),
new MixedRealityInputActionMapping("Trigger Press (Select)", AxisType.Digital, DeviceInputType.Select),
new MixedRealityInputActionMapping("Button.X Press", AxisType.Digital, DeviceInputType.PrimaryButtonPress),
new MixedRealityInputActionMapping("Button.Y Press", AxisType.Digital, DeviceInputType.SecondaryButtonPress),
new MixedRealityInputActionMapping("Menu Press", AxisType.Digital, DeviceInputType.Menu),
new MixedRealityInputActionMapping("Thumbstick Position", AxisType.DualAxis, DeviceInputType.ThumbStick),
new MixedRealityInputActionMapping("Thumbstick Press", AxisType.Digital, DeviceInputType.ThumbStickPress),
};
/// <inheritdoc />
protected override MixedRealityInputActionMapping[] DefaultRightHandedMappings => new[]
{
new MixedRealityInputActionMapping("Spatial Pointer", AxisType.SixDof, DeviceInputType.SpatialPointer),
new MixedRealityInputActionMapping("Spatial Grip", AxisType.SixDof, DeviceInputType.SpatialGrip),
new MixedRealityInputActionMapping("Grip Position", AxisType.SingleAxis, DeviceInputType.Grip),
new MixedRealityInputActionMapping("Grip Touch", AxisType.Digital, DeviceInputType.GripTouch),
new MixedRealityInputActionMapping("Grip Press", AxisType.SingleAxis, DeviceInputType.GripPress),
new MixedRealityInputActionMapping("Trigger Position", AxisType.SingleAxis, DeviceInputType.Trigger),
new MixedRealityInputActionMapping("Trigger Touch", AxisType.Digital, DeviceInputType.TriggerTouch),
new MixedRealityInputActionMapping("Trigger Press (Select)", AxisType.Digital, DeviceInputType.Select),
new MixedRealityInputActionMapping("Button.A Press", AxisType.Digital, DeviceInputType.PrimaryButtonPress),
new MixedRealityInputActionMapping("Button.B Press", AxisType.Digital, DeviceInputType.SecondaryButtonPress),
new MixedRealityInputActionMapping("Menu Press", AxisType.Digital, DeviceInputType.Menu),
new MixedRealityInputActionMapping("Thumbstick Position", AxisType.DualAxis, DeviceInputType.ThumbStick),
new MixedRealityInputActionMapping("Thumbstick Press", AxisType.Digital, DeviceInputType.ThumbStickPress),
};
}
}

View File

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

View File

@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Input
{
// TODO - currently not used, consider removing maybe?
/// <summary>
/// The headset definition defines the headset as defined by the SDK / Unity.
/// </summary>
public struct Headset
{
/// <summary>
/// The ID assigned to the Headset
/// </summary>
public string Id { get; set; }
/// <summary>
/// The designated hand that the controller is managing, as defined by the SDK / Unity.
/// </summary>
public SDKType HeadsetSDKType { get; set; }
/// <summary>
/// Indicates whether or not the headset is currently providing position data.
/// </summary>
public bool IsPositionAvailable { get; set; }
/// <summary>
/// Outputs the current position of the headset, as defined by the SDK / Unity.
/// </summary>
public Vector3 Position { get; set; }
/// <summary>
/// Indicates whether or not the headset is currently providing rotation data.
/// </summary>
public bool IsRotationAvailable { get; set; }
/// <summary>
/// Outputs the current rotation of the headset, as defined by the SDK / Unity.
/// </summary>
public Quaternion Rotation { get; set; }
/// <summary>
/// Outputs the current state of the headset, whether it is tracked or not. As defined by the SDK / Unity.
/// </summary>
public TrackingState TrackingState { get; set; }
/// <summary>
/// Indicates whether or not the headset display is opaque. As defined by the SDK / Unity.
/// </summary>
public bool IsOpaque { get; set; }
}
}

View File

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

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// The InputSourceType defines the types of input sources.
/// </summary>
public enum InputSourceType
{
Other = 0,
Hand,
Controller,
Voice,
Head,
Eyes
}
}

View File

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

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Flags used by MixedRealityControllerAttribute.
/// </summary>
[System.Flags]
public enum MixedRealityControllerConfigurationFlags : byte
{
/// <summary>
/// Controllers with custom interaction mappings can have their mappings be added / removed to the
/// controller mapping profile in the property inspector.
/// </summary>
UseCustomInteractionMappings = 1 << 0,
}
}

View File

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

View File

@ -0,0 +1,252 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Runtime.CompilerServices;
using UnityEngine;
[assembly: InternalsVisibleTo("Microsoft.MixedReality.Toolkit.Editor.Inspectors")]
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Used to define a controller or other input device's physical buttons, and other attributes.
/// </summary>
[Serializable]
public struct MixedRealityControllerMapping
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="controllerType">Controller Type to instantiate at runtime.</param>
/// <param name="handedness">The designated hand that the device is managing.</param>
public MixedRealityControllerMapping(Type controllerType, Handedness handedness = Handedness.None) : this()
{
this.controllerType = new SystemType(controllerType);
this.handedness = handedness;
interactions = null;
}
/// <summary>
/// Description of the Device.
/// </summary>
public string Description
{
get
{
string controllerName = "Unknown";
if (controllerType.Type != null)
{
var attr = MixedRealityControllerAttribute.Find(controllerType);
if (attr != null)
{
controllerName = attr.SupportedControllerType.ToString().ToProperCase();
}
}
string handednessText = string.Empty;
switch (handedness)
{
case Handedness.Left:
case Handedness.Right:
handednessText = $"{handedness} Hand ";
// Avoid multiple occurrences of "Hand":
controllerName = controllerName.Replace("Hand", "").Trim();
break;
}
return $"{controllerName} {handednessText}Controller";
}
}
[SerializeField]
[Tooltip("Controller type to instantiate at runtime.")]
[Implements(typeof(IMixedRealityController), TypeGrouping.ByNamespaceFlat)]
private SystemType controllerType;
/// <summary>
/// Controller Type to instantiate at runtime.
/// </summary>
public SystemType ControllerType => controllerType;
public SupportedControllerType SupportedControllerType
{
get
{
if (controllerType.Type != null)
{
var attr = MixedRealityControllerAttribute.Find(controllerType);
if (attr != null)
{
return attr.SupportedControllerType;
}
}
return 0;
}
}
[SerializeField]
[Tooltip("The designated hand that the device is managing.")]
private Handedness handedness;
/// <summary>
/// The designated hand that the device is managing.
/// </summary>
public Handedness Handedness => handedness;
/// <summary>
/// Is this controller mapping using custom interactions?
/// </summary>
public bool HasCustomInteractionMappings
{
get
{
if (controllerType.Type != null)
{
var attr = MixedRealityControllerAttribute.Find(controllerType);
if (attr != null)
{
return attr.Flags.HasFlag(MixedRealityControllerConfigurationFlags.UseCustomInteractionMappings);
}
}
return false;
}
}
[SerializeField]
[Tooltip("Details the list of available buttons / interactions available from the device.")]
private MixedRealityInteractionMapping[] interactions;
/// <summary>
/// Details the list of available buttons / interactions available from the device.
/// </summary>
public MixedRealityInteractionMapping[] Interactions => interactions;
/// <summary>
/// Sets the default interaction mapping based on the current controller type.
/// </summary>
internal void SetDefaultInteractionMapping(bool overwrite = false)
{
if (interactions == null || interactions.Length == 0 || overwrite)
{
MixedRealityInteractionMapping[] defaultMappings = GetDefaultInteractionMappings();
if (defaultMappings != null)
{
interactions = defaultMappings;
}
}
}
internal bool UpdateInteractionSettingsFromDefault()
{
if (interactions == null || interactions.Length == 0) { return false; }
MixedRealityInteractionMapping[] newDefaultInteractions = GetDefaultInteractionMappings();
if (newDefaultInteractions == null)
{
return false;
}
if (interactions.Length != newDefaultInteractions.Length)
{
interactions = CreateNewMatchedMapping(interactions, newDefaultInteractions);
return true;
}
bool updatedMappings = false;
for (int i = 0; i < newDefaultInteractions.Length; i++)
{
MixedRealityInteractionMapping currentMapping = interactions[i];
MixedRealityInteractionMapping currentDefaultMapping = newDefaultInteractions[i];
if (!Equals(currentMapping, currentDefaultMapping))
{
interactions[i] = new MixedRealityInteractionMapping(currentDefaultMapping)
{
MixedRealityInputAction = currentMapping.MixedRealityInputAction
};
updatedMappings = true;
}
}
return updatedMappings;
}
private MixedRealityInteractionMapping[] CreateNewMatchedMapping(MixedRealityInteractionMapping[] interactions, MixedRealityInteractionMapping[] newDefaultInteractions)
{
MixedRealityInteractionMapping[] newDefaultMapping = new MixedRealityInteractionMapping[newDefaultInteractions.Length];
for (int i = 0; i < newDefaultInteractions.Length; i++)
{
for (int j = 0; j < interactions.Length; j++)
{
if (Equals(interactions[j], newDefaultInteractions[i]))
{
newDefaultMapping[i] = new MixedRealityInteractionMapping(newDefaultInteractions[i])
{
MixedRealityInputAction = interactions[j].MixedRealityInputAction
};
break;
}
}
if (newDefaultMapping[i] == null)
{
newDefaultMapping[i] = new MixedRealityInteractionMapping(newDefaultInteractions[i]);
}
}
return newDefaultMapping;
}
private bool Equals(MixedRealityInteractionMapping a, MixedRealityInteractionMapping b)
{
return !(a.Description != b.Description ||
a.AxisType != b.AxisType ||
a.InputType != b.InputType ||
a.KeyCode != b.KeyCode ||
a.AxisCodeX != b.AxisCodeX ||
a.AxisCodeY != b.AxisCodeY ||
a.InvertXAxis != b.InvertXAxis ||
a.InvertYAxis != b.InvertYAxis);
}
private MixedRealityInteractionMapping[] GetDefaultInteractionMappings()
{
if (Activator.CreateInstance(controllerType, TrackingState.NotTracked, handedness, null, null) is BaseController detectedController)
{
switch (handedness)
{
case Handedness.Left:
return detectedController.DefaultLeftHandedInteractions ?? detectedController.DefaultInteractions;
case Handedness.Right:
return detectedController.DefaultRightHandedInteractions ?? detectedController.DefaultInteractions;
default:
return detectedController.DefaultInteractions;
}
}
return null;
}
/// <summary>
/// Synchronizes the input actions of the same physical controller of a different concrete type.
/// </summary>
internal void SynchronizeInputActions(MixedRealityInteractionMapping[] otherControllerMapping)
{
if (otherControllerMapping.Length != interactions.Length)
{
throw new ArgumentException($"otherControllerMapping length {otherControllerMapping.Length} does not match this length {interactions.Length}.");
}
for (int i = 0; i < interactions.Length; i++)
{
interactions[i].MixedRealityInputAction = otherControllerMapping[i].MixedRealityInputAction;
}
}
}
}

View File

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

View File

@ -0,0 +1,230 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#if UNITY_EDITOR
using UnityEditor;
#endif // UNITY_EDITOR
using UnityEngine;
using UnityEngine.Serialization;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// New controller types can be registered by adding the MixedRealityControllerAttribute to
/// the controller class.
/// </summary>
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Controller Mapping Profile", fileName = "MixedRealityControllerMappingProfile", order = (int)CreateProfileMenuItemIndices.ControllerMapping)]
public class MixedRealityControllerMappingProfile : BaseMixedRealityProfile
{
[SerializeField]
[Tooltip("The list of controller mappings your application can use.")]
[FormerlySerializedAs("mixedRealityControllerMappingProfiles")]
private MixedRealityControllerMapping[] mixedRealityControllerMappings = Array.Empty<MixedRealityControllerMapping>();
/// <summary>
/// The list of controller mappings your application can use.
/// </summary>
public MixedRealityControllerMapping[] MixedRealityControllerMappings => mixedRealityControllerMappings;
[Obsolete("MixedRealityControllerMappingProfiles is obsolete. Please use MixedRealityControllerMappings.")]
public MixedRealityControllerMapping[] MixedRealityControllerMappingProfiles => mixedRealityControllerMappings;
#if UNITY_EDITOR
[MenuItem("Mixed Reality/Toolkit/Utilities/Update/Controller Mapping Profiles")]
private static void UpdateAllControllerMappingProfiles()
{
string[] guids = AssetDatabase.FindAssets("t:MixedRealityControllerMappingProfile");
string[] assetPaths = new string[guids.Length];
for (int i = 0; i < guids.Length; i++)
{
string guid = guids[i];
assetPaths[i] = AssetDatabase.GUIDToAssetPath(guid);
MixedRealityControllerMappingProfile asset = AssetDatabase.LoadAssetAtPath(assetPaths[i], typeof(MixedRealityControllerMappingProfile)) as MixedRealityControllerMappingProfile;
List<MixedRealityControllerMapping> updatedMappings = new List<MixedRealityControllerMapping>();
foreach (MixedRealityControllerMapping mapping in asset.MixedRealityControllerMappings)
{
if (mapping.ControllerType.Type == null)
{
continue;
}
if (!mapping.HasCustomInteractionMappings)
{
mapping.UpdateInteractionSettingsFromDefault();
}
updatedMappings.Add(mapping);
}
asset.mixedRealityControllerMappings = updatedMappings.ToArray();
}
AssetDatabase.ForceReserializeAssets(assetPaths);
}
private static Type[] controllerMappingTypes;
public static Type[] ControllerMappingTypes { get { CollectControllerTypes(); return controllerMappingTypes; } }
public static Type[] CustomControllerMappingTypes { get => (from type in ControllerMappingTypes where UsesCustomInteractionMapping(type) select type).ToArray(); }
private static void CollectControllerTypes()
{
if (controllerMappingTypes == null)
{
List<Type> controllerTypes = new List<Type>();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
IEnumerable<Type> types = null;
try
{
types = assembly.ExportedTypes;
}
catch (NotSupportedException)
{
// assembly.ExportedTypes may not be supported.
}
catch (ReflectionTypeLoadException e)
{
// Not all assemblies may load correctly, but even upon encountering error
// some subset may have loaded in.
if (e.Types != null)
{
List<Type> loadedTypes = new List<Type>();
foreach (Type type in e.Types)
{
// According to API docs, this array may contain null values
// so they must be filtered out here.
if (type != null)
{
loadedTypes.Add(type);
}
}
types = loadedTypes;
}
}
if (types != null)
{
foreach (Type type in types)
{
if (type.IsSubclassOf(typeof(BaseController)) &&
MixedRealityControllerAttribute.Find(type) != null)
{
controllerTypes.Add(type);
}
}
}
}
controllerMappingTypes = controllerTypes.ToArray();
}
}
public void Awake()
{
AddMappings();
SortMappings();
}
private void AddMappings()
{
foreach (var controllerType in ControllerMappingTypes)
{
// Don't auto-add custom mappings when migrating, these can be removed by the user in the inspector.
if (UsesCustomInteractionMapping(controllerType))
{
continue;
}
foreach (Handedness handedness in GetSupportedHandedness(controllerType))
{
// Try to find index of mapping in asset.
int idx = Array.FindIndex(MixedRealityControllerMappings, 0, MixedRealityControllerMappings.Length,
profile => profile.ControllerType.Type == controllerType && profile.Handedness == handedness);
if (idx < 0)
{
var newMapping = new MixedRealityControllerMapping(controllerType, handedness);
newMapping.SetDefaultInteractionMapping(overwrite: false);
// Re-use existing mapping with the same supported controller type.
foreach (var otherMapping in mixedRealityControllerMappings)
{
if (otherMapping.SupportedControllerType == newMapping.SupportedControllerType &&
otherMapping.Handedness == newMapping.Handedness)
{
try
{
newMapping.SynchronizeInputActions(otherMapping.Interactions);
}
catch (ArgumentException e)
{
Debug.LogError($"Controller mappings between {newMapping.Description} and {otherMapping.Description} do not match. Error message: {e.Message}");
}
break;
}
}
idx = mixedRealityControllerMappings.Length;
Array.Resize(ref mixedRealityControllerMappings, idx + 1);
mixedRealityControllerMappings[idx] = newMapping;
}
else
{
mixedRealityControllerMappings[idx].UpdateInteractionSettingsFromDefault();
}
}
}
}
private void SortMappings()
{
Array.Sort(mixedRealityControllerMappings, (profile1, profile2) =>
{
bool isOptional1 = (profile1.ControllerType.Type == null || profile1.HasCustomInteractionMappings);
bool isOptional2 = (profile2.ControllerType.Type == null || profile2.HasCustomInteractionMappings);
if (!isOptional1 && !isOptional2)
{
int idx1 = Array.FindIndex(ControllerMappingTypes, type => type == profile1.ControllerType.Type);
int idx2 = Array.FindIndex(ControllerMappingTypes, type => type == profile2.ControllerType.Type);
if (idx1 == idx2)
{
idx1 = (int)profile1.Handedness;
idx2 = (int)profile2.Handedness;
}
return Math.Sign(idx1 - idx2);
}
if (isOptional1 && isOptional2)
{
return 0;
}
return isOptional1 ? 1 : -1; // Put custom mappings at the end. These can be added / removed in the inspector.
});
}
private static bool UsesCustomInteractionMapping(Type controllerType)
{
var attribute = MixedRealityControllerAttribute.Find(controllerType);
return attribute != null && attribute.Flags.HasFlag(MixedRealityControllerConfigurationFlags.UseCustomInteractionMappings);
}
private static Handedness[] GetSupportedHandedness(Type controllerType)
{
var attribute = MixedRealityControllerAttribute.Find(controllerType);
return attribute != null ? attribute.SupportedHandedness : Array.Empty<Handedness>();
}
#endif // UNITY_EDITOR
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a1cf5fd47e548e6a23bdd3ddcc00cf6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Provides helpers for setting up the controller model with a visualization script.
/// </summary>
public static class MixedRealityControllerModelHelpers
{
private static MixedRealityControllerVisualizationProfile visualizationProfile = null;
/// <summary>
/// Tries to read the controller visualization profile to apply a visualization script to the passed-in controller model.
/// </summary>
/// <remarks>Automatically disables DestroyOnSourceLost to encourage controller model creators to manage their life-cycle themselves.</remarks>
/// <param name="controllerModel">The GameObject to modify.</param>
/// <param name="controllerType">The type of controller this model represents.</param>
/// <param name="handedness">The handedness of this controller.</param>
/// <returns>True if a visualization script could be loaded and applied.</returns>
public static bool TryAddVisualizationScript(GameObject controllerModel, Type controllerType, Handedness handedness)
{
if (controllerModel != null)
{
if (visualizationProfile == null && CoreServices.InputSystem?.InputSystemProfile != null)
{
visualizationProfile = CoreServices.InputSystem.InputSystemProfile.ControllerVisualizationProfile;
}
if (visualizationProfile != null)
{
var visualizationType = visualizationProfile.GetControllerVisualizationTypeOverride(controllerType, handedness);
if (visualizationType != null)
{
// Set the platform controller model to not be destroyed when the source is lost. It'll be disabled instead,
// and re-enabled when the same controller is re-detected.
if (controllerModel.EnsureComponent(visualizationType.Type) is IMixedRealityControllerPoseSynchronizer visualizer)
{
visualizer.DestroyOnSourceLost = false;
}
return true;
}
else
{
Debug.LogError("Controller visualization type not defined for controller visualization profile");
}
}
else
{
Debug.LogError("Failed to obtain a controller visualization profile");
}
}
return false;
}
}
}

View File

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

View File

@ -0,0 +1,254 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
using UnityEngine.Serialization;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Profile that determines relevant overrides and properties for controller visualization
/// </summary>
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Controller Visualization Profile", fileName = "MixedRealityControllerVisualizationProfile", order = (int)CreateProfileMenuItemIndices.ControllerVisualization)]
[MixedRealityServiceProfile(typeof(IMixedRealityControllerVisualizer))]
public class MixedRealityControllerVisualizationProfile : BaseMixedRealityProfile
{
[SerializeField]
[Tooltip("Enable and configure the controller rendering of the Motion Controllers on Startup.")]
private bool renderMotionControllers = false;
/// <summary>
/// Enable and configure the controller rendering of the Motion Controllers on Startup.
/// </summary>
public bool RenderMotionControllers
{
get => renderMotionControllers;
private set => renderMotionControllers = value;
}
[SerializeField]
[Implements(typeof(IMixedRealityControllerVisualizer), TypeGrouping.ByNamespaceFlat)]
[Tooltip("The default controller visualization type. This value is used as a fallback if no controller definition exists with a custom visualization type.")]
private SystemType defaultControllerVisualizationType;
/// <summary>
/// The default controller visualization type. This value is used as a fallback if no controller definition exists with a custom visualization type.
/// </summary>
public SystemType DefaultControllerVisualizationType
{
get => defaultControllerVisualizationType;
private set => defaultControllerVisualizationType = value;
}
[SerializeField]
[Tooltip("Check to obtain controller models from the platform SDK. If left unchecked, the global models will be used. Note: this value is overridden by controller definitions.")]
[FormerlySerializedAs("useDefaultModels")]
private bool usePlatformModels = false;
/// <summary>
/// Check to obtain controller models from the platform SDK. If left unchecked, the global models will be used. Note: this value is overridden by controller definitions.
/// </summary>
public bool UsePlatformModels
{
get => usePlatformModels;
private set => usePlatformModels = value;
}
/// <summary>
/// Check to obtain controller models from the platform SDK. If left unchecked, the global models will be used. Note: this value is overridden by controller definitions.
/// </summary>
[Obsolete("Use UsePlatformModels instead.")]
public bool UseDefaultModels => usePlatformModels;
[SerializeField]
[Tooltip("The default controller model material when loading platform SDK controller models. This value is used as a fallback if no controller definition exists with a custom material type.")]
[FormerlySerializedAs("defaultControllerModelMaterial")]
private Material platformModelMaterial;
/// <summary>
/// The default controller model material when loading platform SDK controller models. This value is used as a fallback if no controller definition exists with a custom material type.
/// </summary>
public Material PlatformModelMaterial
{
get => platformModelMaterial;
private set => platformModelMaterial = value;
}
/// <summary>
/// The default controller model material when loading platform SDK controller models. This value is used as a fallback if no controller definition exists with a custom material type.
/// </summary>
[Obsolete("Use PlatformModelMaterial instead.")]
public Material DefaultControllerModelMaterial => platformModelMaterial;
[SerializeField]
[Tooltip("Override Left Controller Model.")]
private GameObject globalLeftControllerModel;
/// <summary>
/// The Default controller model when there is no specific controller model for the Left hand or when no hand is specified (Handedness = none)
/// </summary>
/// <remarks>
/// If the default model for the left hand controller can not be found, the controller will fall back and use this for visualization.
/// </remarks>
public GameObject GlobalLeftHandModel
{
get => globalLeftControllerModel;
private set => globalLeftControllerModel = value;
}
[SerializeField]
[Tooltip("Override Right Controller Model.\nNote: If the default model is not found, the fallback is the global right hand model.")]
private GameObject globalRightControllerModel;
/// <summary>
/// The Default controller model when there is no specific controller model for the Right hand.
/// </summary>
/// <remarks>
/// If the default model for the right hand controller can not be found, the controller will fall back and use this for visualization.
/// </remarks>
public GameObject GlobalRightHandModel
{
get => globalRightControllerModel;
private set => globalRightControllerModel = value;
}
[SerializeField]
[Tooltip("Override Left Hand Visualizer.")]
private GameObject globalLeftHandVisualizer;
/// <summary>
/// The Default controller model when there is no specific controller model for the Left hand or when no hand is specified (Handedness = none)
/// </summary>
/// <remarks>
/// If the default model for the left hand controller can not be found, the controller will fall back and use this for visualization.
/// </remarks>
public GameObject GlobalLeftHandVisualizer
{
get => globalLeftHandVisualizer;
private set => globalLeftHandVisualizer = value;
}
[SerializeField]
[Tooltip("Override Right Controller Model.\nNote: If the default model is not found, the fallback is the global right hand model.")]
private GameObject globalRightHandVisualizer;
/// <summary>
/// The Default hand model when there is no specific hand model for the Right hand.
/// </summary>
/// <remarks>
/// If the default model for the right hand can not be found, the hand will fall back and use this for visualization.
/// </remarks>
public GameObject GlobalRightHandVisualizer
{
get => globalRightHandVisualizer;
private set => globalRightHandVisualizer = value;
}
[SerializeField]
private MixedRealityControllerVisualizationSetting[] controllerVisualizationSettings = Array.Empty<MixedRealityControllerVisualizationSetting>();
/// <summary>
/// The current list of controller visualization settings.
/// </summary>
public MixedRealityControllerVisualizationSetting[] ControllerVisualizationSettings => controllerVisualizationSettings;
private MixedRealityControllerVisualizationSetting? GetControllerVisualizationDefinition(Type controllerType, Handedness hand)
{
for (int i = 0; i < controllerVisualizationSettings.Length; i++)
{
if (SettingContainsParameters(controllerVisualizationSettings[i], controllerType, hand))
{
return controllerVisualizationSettings[i];
}
}
return null;
}
/// <summary>
/// Gets the override model for a specific controller type and hand
/// </summary>
/// <param name="controllerType">The type of controller to query for</param>
/// <param name="hand">The specific hand assigned to the controller</param>
public GameObject GetControllerModelOverride(Type controllerType, Handedness hand)
{
MixedRealityControllerVisualizationSetting? setting = GetControllerVisualizationDefinition(controllerType, hand);
return setting.HasValue ? setting.Value.OverrideControllerModel : null;
}
/// <summary>
/// Gets the override <see cref="IMixedRealityControllerVisualizer"/> type for a specific controller type and hand.
/// If the requested controller type is not defined, DefaultControllerVisualizationType is returned.
/// </summary>
/// <param name="controllerType">The type of controller to query for</param>
/// <param name="hand">The specific hand assigned to the controller</param>
public SystemType GetControllerVisualizationTypeOverride(Type controllerType, Handedness hand)
{
MixedRealityControllerVisualizationSetting? setting = GetControllerVisualizationDefinition(controllerType, hand);
return setting.HasValue ? setting.Value.ControllerVisualizationType : DefaultControllerVisualizationType;
}
/// <summary>
/// Gets the UsePlatformModels value defined for the specified controller definition.
/// If the requested controller type is not defined, the default UsePlatformModels is returned.
/// </summary>
/// <param name="controllerType">The type of controller to query for</param>
/// <param name="hand">The specific hand assigned to the controller</param>
/// <remarks>
/// GetUseDefaultModelsOverride is obsolete and will be removed in a future Mixed Reality Toolkit release. Please use GetUsePlatformModelsOverride.
/// </remarks>
[Obsolete("GetUseDefaultModelsOverride is obsolete and will be removed in a future Mixed Reality Toolkit release. Please use GetUsePlatformModelsOverride.")]
public bool GetUseDefaultModelsOverride(Type controllerType, Handedness hand)
{
return GetUsePlatformModelsOverride(controllerType, hand);
}
/// <summary>
/// Gets the UsePlatformModels value defined for the specified controller definition.
/// If the requested controller type is not defined, the default UsePlatformModels is returned.
/// </summary>
/// <param name="controllerType">The type of controller to query for</param>
/// <param name="hand">The specific hand assigned to the controller</param>
public bool GetUsePlatformModelsOverride(Type controllerType, Handedness hand)
{
MixedRealityControllerVisualizationSetting? setting = GetControllerVisualizationDefinition(controllerType, hand);
return setting.HasValue ? setting.Value.UsePlatformModels : usePlatformModels;
}
/// <summary>
/// Gets the DefaultModelMaterial value defined for the specified controller definition.
/// If the requested controller type is not defined, the global platformModelMaterial is returned.
/// </summary>
/// <param name="controllerType">The type of controller to query for</param>
/// <param name="hand">The specific hand assigned to the controller</param>
/// <remarks>
/// GetDefaultControllerModelMaterialOverride is obsolete and will be removed in a future Mixed Reality Toolkit release. Please use GetPlatformModelMaterialOverride.
/// </remarks>
[Obsolete("GetDefaultControllerModelMaterialOverride is obsolete and will be removed in a future Mixed Reality Toolkit release. Please use GetPlatformModelMaterial.")]
public Material GetDefaultControllerModelMaterialOverride(Type controllerType, Handedness hand)
{
return GetPlatformModelMaterialOverride(controllerType, hand);
}
/// <summary>
/// Gets the PlatformModelMaterial value defined for the specified controller definition.
/// If the requested controller type is not defined, the global platformModelMaterial is returned.
/// </summary>
/// <param name="controllerType">The type of controller to query for</param>
/// <param name="hand">The specific hand assigned to the controller</param>
public Material GetPlatformModelMaterialOverride(Type controllerType, Handedness hand)
{
MixedRealityControllerVisualizationSetting? setting = GetControllerVisualizationDefinition(controllerType, hand);
return setting.HasValue ? setting.Value.PlatformModelMaterial : platformModelMaterial;
}
private bool SettingContainsParameters(MixedRealityControllerVisualizationSetting setting, Type controllerType, Handedness hand)
{
return setting.ControllerType != null &&
setting.ControllerType.Type == controllerType &&
setting.Handedness.IsMaskSet(hand) && setting.Handedness != Handedness.None;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 514da61389c049c0bdaf31b7f6970d33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
using UnityEngine.Serialization;
namespace Microsoft.MixedReality.Toolkit.Input
{
/// <summary>
/// Used to define a controller's visualization settings.
/// </summary>
[Serializable]
public struct MixedRealityControllerVisualizationSetting
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="description">Description of the Device.</param>
/// <param name="controllerType">Controller Type to instantiate at runtime.</param>
/// <param name="handedness">The designated hand that the device is managing.</param>
/// <param name="overrideModel">The controller model prefab to be rendered.</param>
[Obsolete("This constructor doesn't need to be called directly.")]
public MixedRealityControllerVisualizationSetting(string description, Type controllerType, Handedness handedness = Handedness.None, GameObject overrideModel = null) : this()
{
this.description = description;
this.controllerType = new SystemType(controllerType);
this.handedness = handedness;
this.overrideModel = overrideModel;
usePlatformModels = false;
platformModelMaterial = null;
}
[SerializeField]
private string description;
/// <summary>
/// Description of the Device.
/// </summary>
public string Description => description;
[SerializeField]
[Tooltip("Controller type to instantiate at runtime.")]
[Implements(typeof(IMixedRealityController), TypeGrouping.ByNamespaceFlat)]
private SystemType controllerType;
/// <summary>
/// Controller Type to instantiate at runtime.
/// </summary>
public SystemType ControllerType => controllerType;
[SerializeField]
[Tooltip("The designated hand that the device is managing.")]
private Handedness handedness;
/// <summary>
/// The designated hand that the device is managing.
/// </summary>
public Handedness Handedness => handedness;
[SerializeField]
[Tooltip("Check to obtain controller models from the platform SDK. If left unchecked, the global models will be used.")]
[FormerlySerializedAs("useDefaultModel")]
private bool usePlatformModels;
/// <summary>
/// Check to obtain controller models from the platform SDK. If left unchecked, the global models will be used.
/// </summary>
public bool UsePlatformModels => usePlatformModels;
[SerializeField]
[Tooltip("The default controller model material when loading platform SDK controller models.")]
[FormerlySerializedAs("defaultModelMaterial")]
private Material platformModelMaterial;
/// <summary>
/// The default controller model material when loading platform SDK controller models. This value is used as a fallback if no controller definition exists with a custom material type.
/// </summary>
public Material PlatformModelMaterial => platformModelMaterial;
[SerializeField]
[Tooltip("An override model to display for this specific controller.")]
private GameObject overrideModel;
/// <summary>
/// The controller model prefab to be rendered.
/// </summary>
public GameObject OverrideControllerModel => overrideModel;
[SerializeField]
[Tooltip("The concrete Controller Visualizer component to use on the rendered controller model.")]
[Implements(typeof(IMixedRealityControllerVisualizer), TypeGrouping.ByNamespaceFlat)]
private SystemType controllerVisualizationType;
/// <summary>
/// The concrete Controller Visualizer component to use on the rendered controller model
/// </summary>
public SystemType ControllerVisualizationType
{
get => controllerVisualizationType;
private set => controllerVisualizationType = value;
}
}
}

View File

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

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Input
{
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Eye Tracking Profile", fileName = "MixedRealityEyeTrackingProfile", order = (int)CreateProfileMenuItemIndices.EyeTracking)]
[MixedRealityServiceProfile(requiredTypes: new Type[] { typeof(IMixedRealityEyeGazeDataProvider), typeof(IMixedRealityEyeSaccadeProvider) })]
public class MixedRealityEyeTrackingProfile : BaseMixedRealityProfile
{
[SerializeField]
[Tooltip("Use smoothed eye tracking signal.")]
private bool smoothEyeTracking = false;
/// <summary>
/// Use smoothed eye tracking signal.
/// </summary>
public bool SmoothEyeTracking => smoothEyeTracking;
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e534d3d8fbada634785d567d8fe8562c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,159 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Input
{
[CreateAssetMenu(menuName = "Mixed Reality/Toolkit/Profiles/Mixed Reality Hand Tracking Profile", fileName = "MixedRealityHandTrackingProfile", order = (int)CreateProfileMenuItemIndices.HandTracking)]
[HelpURL("https://docs.microsoft.com/windows/mixed-reality/mrtk-unity/features/input/hand-tracking")]
public class MixedRealityHandTrackingProfile : BaseMixedRealityProfile
{
[SerializeField]
[Tooltip("The joint prefab to use.")]
private GameObject jointPrefab = null;
[SerializeField]
[Tooltip("The joint prefab to use for palm.")]
private GameObject palmPrefab = null;
[SerializeField]
[Tooltip("The joint prefab to use for the index tip (point of interaction.")]
private GameObject fingertipPrefab = null;
/// <summary>
/// The joint prefab to use.
/// </summary>
public GameObject JointPrefab => jointPrefab;
/// <summary>
/// The joint prefab to use for palm
/// </summary>
public GameObject PalmJointPrefab => palmPrefab;
/// <summary>
/// The joint prefab to use for finger tip
/// </summary>
public GameObject FingerTipPrefab => fingertipPrefab;
[SerializeField]
[Tooltip("The hand mesh material to use for system generated hand meshes")]
private Material systemHandMeshMaterial;
/// <summary>
/// The hand mesh material to use for system generated hand meshes
/// </summary>
public Material SystemHandMeshMaterial => systemHandMeshMaterial;
[SerializeField]
[Tooltip("The hand mesh material to use for rigged hand meshes")]
private Material riggedHandMeshMaterial;
/// <summary>
/// The hand mesh material to use for rigged hand meshes
/// </summary>
public Material RiggedHandMeshMaterial => riggedHandMeshMaterial;
[SerializeField]
[Tooltip("If this is not null and hand system supports hand meshes, use this mesh to render hand mesh.")]
private GameObject handMeshPrefab = null;
/// <summary>
/// The hand mesh prefab to use to render the hand
/// </summary>
[Obsolete("The GameObject which generates the system handmesh is now created at runtime. This prefab is not used")]
public GameObject HandMeshPrefab => handMeshPrefab;
/// <summary>
/// The hand mesh visualization enable/disable state of the current application mode.
/// </summary>
/// <remarks>
/// <para>If this property is called while in-editor, this will only affect the in-editor settings
/// (i.e. the SupportedApplicationModes.Editor flag of HandMeshVisualizationModes).</para>
/// <para>If this property is called while in-player, this will only affect the in-player settings
/// (i.e. the SupportedApplicationModes.Player flag of HandMeshVisualizationModes).</para>
/// </remarks>
public bool EnableHandMeshVisualization
{
get => IsSupportedApplicationMode(handMeshVisualizationModes);
set => handMeshVisualizationModes = UpdateSupportedApplicationMode(value, handMeshVisualizationModes);
}
/// <summary>
/// The hand joint visualization enable/disable state of the current application mode.
/// </summary>
/// <remarks>
/// <para>If this property is called while in-editor, this will only affect the in-editor settings
/// (i.e. the SupportedApplicationModes.Editor flag of HandJointVisualizationModes).</para>
/// <para>If this property is called while in-player, this will only affect the in-player settings
/// (i.e. the SupportedApplicationModes.Player flag of HandJointVisualizationModes).</para>
/// </remarks>
public bool EnableHandJointVisualization
{
get => IsSupportedApplicationMode(handJointVisualizationModes);
set => handJointVisualizationModes = UpdateSupportedApplicationMode(value, handJointVisualizationModes);
}
[EnumFlags]
[SerializeField]
[Tooltip("The application modes in which hand mesh visualizations will display. " +
"Will only show if the system provides hand mesh data. Note: this could reduce performance")]
private SupportedApplicationModes handMeshVisualizationModes = 0;
public SupportedApplicationModes HandMeshVisualizationModes
{
get => handMeshVisualizationModes;
set => handMeshVisualizationModes = value;
}
[EnumFlags]
[SerializeField]
[Tooltip("The application modes in which hand joint visualizations will display. " +
"Will only show if the system provides joint data. Note: this could reduce performance")]
private SupportedApplicationModes handJointVisualizationModes = 0;
public SupportedApplicationModes HandJointVisualizationModes
{
get => handJointVisualizationModes;
set => handJointVisualizationModes = value;
}
/// <summary>
/// Returns true if the modes specified by the specified SupportedApplicationModes matches
/// the current mode that the code is running in.
/// </summary>
/// <remarks>
/// <para>For example, if the code is currently running in editor mode (for testing in-editor
/// simulation), this would return true if modes contained the SupportedApplicationModes.Editor
/// bit.</para>
/// </remarks>
private static bool IsSupportedApplicationMode(SupportedApplicationModes modes)
{
#if UNITY_EDITOR
return (modes & SupportedApplicationModes.Editor) != 0;
#else // !UNITY_EDITOR
return (modes & SupportedApplicationModes.Player) != 0;
#endif
}
/// <summary>
/// Updates the given SupportedApplicationModes by setting the bit associated with the
/// currently active application mode.
/// </summary>
/// <remarks>
/// <para>For example, if the code is currently running in editor mode (for testing in-editor
/// simulation), and modes is currently SupportedApplicationModes.Player | SupportedApplicationModes.Editor
/// and enabled is 'false', this would return SupportedApplicationModes.Player.</para>
/// </remarks>
private static SupportedApplicationModes UpdateSupportedApplicationMode(bool enabled, SupportedApplicationModes modes)
{
#if UNITY_EDITOR
var bitValue = enabled ? SupportedApplicationModes.Editor : 0;
return (modes & ~SupportedApplicationModes.Editor) | bitValue;
#else // !UNITY_EDITOR
var bitValue = enabled ? SupportedApplicationModes.Player : 0;
return (modes & ~SupportedApplicationModes.Player) | bitValue;
#endif
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8275efdbe76bdff49a97a8e82fba118d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 4f9f54f9478441228dea18a2c828cfc6, type: 3}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More