mixedreality/com.microsoft.mixedreality..../Core/Definitions/Devices/MixedRealityControllerMappi...

230 lines
9.8 KiB
C#

// 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
}
}