public static void Show(MixedRealityControllerMapping controllerMapping, SerializedProperty interactionsList, Handedness handedness = Handedness.None) { if (window != null) { window.Close(); } window = null; window = CreateInstance <ControllerPopupWindow>(); window.thisWindow = window; window.titleContent = new GUIContent($"{controllerMapping.Description} - Input Action Assignment"); window.currentControllerMapping = controllerMapping; window.currentInteractionList = interactionsList; isMouseInRects = new bool[interactionsList.arraySize]; if (!File.Exists($"{Application.dataPath}{EditorWindowOptionsPath}")) { var empty = new ControllerInputActionOptions { Controllers = new List <ControllerInputActionOption> { new ControllerInputActionOption { Controller = 0, Handedness = Handedness.None, InputLabelPositions = new[] { new Vector2(0, 0) }, IsLabelFlipped = new [] { false } } } }; File.WriteAllText($"{Application.dataPath}{EditorWindowOptionsPath}", JsonUtility.ToJson(empty)); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); } else { controllerInputActionOptions = JsonUtility.FromJson <ControllerInputActionOptions>(File.ReadAllText($"{Application.dataPath}{EditorWindowOptionsPath}")); if (controllerInputActionOptions.Controllers.Any(option => option.Controller == controllerMapping.SupportedControllerType && option.Handedness == handedness)) { window.currentControllerOption = controllerInputActionOptions.Controllers.FirstOrDefault(option => option.Controller == controllerMapping.SupportedControllerType && option.Handedness == handedness); if (window.currentControllerOption != null && window.currentControllerOption.IsLabelFlipped == null) { window.currentControllerOption.IsLabelFlipped = new bool[interactionsList.arraySize]; } } } var windowSize = new Vector2(controllerMapping.HasCustomInteractionMappings ? 896f : 768f, 512f); window.maxSize = windowSize; window.minSize = windowSize; window.CenterOnMainWin(); window.ShowUtility(); defaultLabelWidth = EditorGUIUtility.labelWidth; defaultFieldWidth = EditorGUIUtility.fieldWidth; }
public void TestControllerMappingProfileUpdate() { MixedRealityControllerMapping[] testMappingsChanged = new MixedRealityControllerMapping[] { new MixedRealityControllerMapping(typeof(ViveWandController), Microsoft.MixedReality.Toolkit.Utilities.Handedness.Left), new MixedRealityControllerMapping(typeof(ViveWandController), Microsoft.MixedReality.Toolkit.Utilities.Handedness.Right) }; testMappingsChanged[0].SetDefaultInteractionMapping(); testMappingsChanged[1].SetDefaultInteractionMapping(); testMappingsChanged[0].Interactions[1] = new MixedRealityInteractionMapping(1, "Fake mapping", Microsoft.MixedReality.Toolkit.Utilities.AxisType.Digital, DeviceInputType.ButtonNearTouch); bool wereMappingsUpdated = false; foreach (MixedRealityControllerMapping mapping in testMappingsChanged) { if (mapping.UpdateInteractionSettingsFromDefault()) { wereMappingsUpdated = true; } } Assert.IsTrue(wereMappingsUpdated, "No mappings were updated. This test should always need an update."); }
private void RenderControllerList(SerializedProperty controllerList) { if (thisProfile.MixedRealityControllerMappingProfiles.Length != controllerList.arraySize) { return; } EditorGUILayout.Space(); if (GUILayout.Button(ControllerAddButtonContent, EditorStyles.miniButton)) { AddController(controllerList, typeof(GenericJoystickController)); return; } controllerRenderList.Clear(); GUILayout.Space(12f); using (var outerVerticalScope = new GUILayout.VerticalScope()) { GUILayout.HorizontalScope horizontalScope = null; for (int i = 0; i < thisProfile.MixedRealityControllerMappingProfiles.Length; i++) { MixedRealityControllerMapping controllerMapping = thisProfile.MixedRealityControllerMappingProfiles[i]; Type controllerType = controllerMapping.ControllerType; if (controllerType == null) { continue; } Handedness handedness = controllerMapping.Handedness; bool useCustomInteractionMappings = controllerMapping.HasCustomInteractionMappings; SupportedControllerType supportedControllerType = controllerMapping.SupportedControllerType; var controllerMappingProperty = controllerList.GetArrayElementAtIndex(i); var handednessProperty = controllerMappingProperty.FindPropertyRelative("handedness"); if (!useCustomInteractionMappings) { bool skip = false; // Merge controllers with the same supported controller type. for (int j = 0; j < controllerRenderList.Count; j++) { if (controllerRenderList[j].SupportedControllerType == supportedControllerType && controllerRenderList[j].Handedness == handedness) { thisProfile.MixedRealityControllerMappingProfiles[i].SynchronizeInputActions(controllerRenderList[j].Interactions); serializedObject.ApplyModifiedProperties(); skip = true; } } if (skip) { continue; } } controllerRenderList.Add(new ControllerRenderProfile(supportedControllerType, handedness, thisProfile.MixedRealityControllerMappingProfiles[i].Interactions)); string controllerTitle = thisProfile.MixedRealityControllerMappingProfiles[i].Description; var interactionsProperty = controllerMappingProperty.FindPropertyRelative("interactions"); if (useCustomInteractionMappings) { if (horizontalScope != null) { horizontalScope.Dispose(); horizontalScope = null; } GUILayout.Space(24f); using (var verticalScope = new GUILayout.VerticalScope()) { using (horizontalScope = new GUILayout.HorizontalScope()) { EditorGUIUtility.labelWidth = 64f; EditorGUIUtility.fieldWidth = 64f; EditorGUILayout.LabelField(controllerTitle); EditorGUIUtility.fieldWidth = defaultFieldWidth; EditorGUIUtility.labelWidth = defaultLabelWidth; if (GUILayout.Button(ControllerMinusButtonContent, EditorStyles.miniButtonRight, GUILayout.Width(24f))) { controllerList.DeleteArrayElementAtIndex(i); return; } } EditorGUI.indentLevel++; EditorGUIUtility.labelWidth = 128f; EditorGUIUtility.fieldWidth = 64f; EditorGUI.BeginChangeCheck(); // Generic Type dropdown Type[] genericTypes = MixedRealityControllerMappingProfile.CustomControllerMappingTypes; var genericTypeListContent = new GUIContent[genericTypes.Length]; var genericTypeListIds = new int[genericTypes.Length]; int currentGenericType = -1; for (int genericTypeIdx = 0; genericTypeIdx < genericTypes.Length; genericTypeIdx++) { var attribute = MixedRealityControllerAttribute.Find(genericTypes[genericTypeIdx]); if (attribute != null) { genericTypeListContent[genericTypeIdx] = new GUIContent(attribute.SupportedControllerType.ToString().Replace("Generic", "").ToProperCase() + " Controller"); } else { genericTypeListContent[genericTypeIdx] = new GUIContent("Unknown Controller"); } genericTypeListIds[genericTypeIdx] = genericTypeIdx; if (controllerType == genericTypes[genericTypeIdx]) { currentGenericType = genericTypeIdx; } } Debug.Assert(currentGenericType != -1); currentGenericType = EditorGUILayout.IntPopup(GenericTypeContent, currentGenericType, genericTypeListContent, genericTypeListIds); controllerType = genericTypes[currentGenericType]; { // Handedness dropdown var attribute = MixedRealityControllerAttribute.Find(controllerType); if (attribute != null && attribute.SupportedHandedness.Length >= 1) { // Make sure handedness is valid for the selected controller type. if (Array.IndexOf(attribute.SupportedHandedness, (Handedness)handednessProperty.intValue) < 0) { handednessProperty.intValue = (int)attribute.SupportedHandedness[0]; } if (attribute.SupportedHandedness.Length >= 2) { var handednessListContent = new GUIContent[attribute.SupportedHandedness.Length]; var handednessListIds = new int[attribute.SupportedHandedness.Length]; for (int handednessIdx = 0; handednessIdx < attribute.SupportedHandedness.Length; handednessIdx++) { handednessListContent[handednessIdx] = new GUIContent(attribute.SupportedHandedness[handednessIdx].ToString()); handednessListIds[handednessIdx] = (int)attribute.SupportedHandedness[handednessIdx]; } handednessProperty.intValue = EditorGUILayout.IntPopup(HandednessTypeContent, handednessProperty.intValue, handednessListContent, handednessListIds); } } else { handednessProperty.intValue = (int)Handedness.None; } } if (EditorGUI.EndChangeCheck()) { interactionsProperty.ClearArray(); serializedObject.ApplyModifiedProperties(); thisProfile.MixedRealityControllerMappingProfiles[i].ControllerType.Type = genericTypes[currentGenericType]; thisProfile.MixedRealityControllerMappingProfiles[i].SetDefaultInteractionMapping(true); serializedObject.ApplyModifiedProperties(); return; } EditorGUIUtility.labelWidth = defaultLabelWidth; EditorGUIUtility.fieldWidth = defaultFieldWidth; EditorGUI.indentLevel--; if (GUILayout.Button("Edit Input Action Map")) { ControllerPopupWindow.Show(controllerMapping, interactionsProperty, handedness); } if (GUILayout.Button("Reset Input Actions")) { interactionsProperty.ClearArray(); serializedObject.ApplyModifiedProperties(); thisProfile.MixedRealityControllerMappingProfiles[i].SetDefaultInteractionMapping(true); serializedObject.ApplyModifiedProperties(); } } } else { if (supportedControllerType == SupportedControllerType.WindowsMixedReality && handedness == Handedness.None) { controllerTitle = "HoloLens Gestures"; } if (handedness != Handedness.Right) { if (horizontalScope != null) { horizontalScope.Dispose(); horizontalScope = null; } horizontalScope = new GUILayout.HorizontalScope(); } var buttonContent = new GUIContent(controllerTitle, ControllerMappingLibrary.GetControllerTextureScaled(controllerType, handedness)); if (GUILayout.Button(buttonContent, controllerButtonStyle, GUILayout.Height(128f), GUILayout.MinWidth(32f), GUILayout.ExpandWidth(true))) { ControllerPopupWindow.Show(controllerMapping, interactionsProperty, handedness); } } } if (horizontalScope != null) { horizontalScope.Dispose(); horizontalScope = null; } } }
private void RenderControllerList(SerializedProperty controllerList) { if (thisProfile.MixedRealityControllerMappings.Length != controllerList.arraySize) { return; } if (InspectorUIUtility.RenderIndentedButton(ControllerAddButtonContent, EditorStyles.miniButton)) { AddController(controllerList, typeof(GenericJoystickController)); return; } controllerRenderList.Clear(); // Generating the set of controllers that belong to each Controller Mapping Signature Dictionary <ControllerMappingSignature, List <string> > controllersAffectedByMappingSignatures = new Dictionary <ControllerMappingSignature, List <string> >(); for (int i = 0; i < thisProfile.MixedRealityControllerMappings.Length; i++) { MixedRealityControllerMapping controllerMapping = thisProfile.MixedRealityControllerMappings[i]; Type controllerType = controllerMapping.ControllerType; if (controllerType == null) { continue; } Handedness handedness = controllerMapping.Handedness; SupportedControllerType supportedControllerType = controllerMapping.SupportedControllerType; ControllerMappingSignature currentSignature = new ControllerMappingSignature(supportedControllerType, handedness); if (!controllersAffectedByMappingSignatures.ContainsKey(currentSignature)) { controllersAffectedByMappingSignatures.Add(currentSignature, new List <string>()); } controllersAffectedByMappingSignatures[currentSignature].Add(controllerType.ToString()); } showControllerDefinitions = EditorGUILayout.Foldout(showControllerDefinitions, "Controller Definitions", true); if (showControllerDefinitions) { using (var outerVerticalScope = new GUILayout.VerticalScope()) { GUILayout.HorizontalScope horizontalScope = null; for (int i = 0; i < thisProfile.MixedRealityControllerMappings.Length; i++) { MixedRealityControllerMapping controllerMapping = thisProfile.MixedRealityControllerMappings[i]; Type controllerType = controllerMapping.ControllerType; if (controllerType == null) { continue; } Handedness handedness = controllerMapping.Handedness; bool useCustomInteractionMappings = controllerMapping.HasCustomInteractionMappings; SupportedControllerType supportedControllerType = controllerMapping.SupportedControllerType; var controllerMappingProperty = controllerList.GetArrayElementAtIndex(i); var handednessProperty = controllerMappingProperty.FindPropertyRelative("handedness"); #region Profile Migration // Between MRTK v2 RC2 and GA, the HoloLens clicker and HoloLens voice select input were migrated from // SupportedControllerType.WindowsMixedReality && Handedness.None to SupportedControllerType.GGVHand && Handedness.None if (supportedControllerType == SupportedControllerType.WindowsMixedReality && handedness == Handedness.None) { for (int j = 0; j < thisProfile.MixedRealityControllerMappings.Length; j++) { if (thisProfile.MixedRealityControllerMappings[j].SupportedControllerType == SupportedControllerType.GGVHand && thisProfile.MixedRealityControllerMappings[j].Handedness == Handedness.None) { if (horizontalScope != null) { horizontalScope.Dispose(); horizontalScope = null; } serializedObject.ApplyModifiedProperties(); for (int k = 0; k < controllerMapping.Interactions.Length; k++) { MixedRealityInteractionMapping currentMapping = controllerMapping.Interactions[k]; if (currentMapping.InputType == DeviceInputType.Select) { thisProfile.MixedRealityControllerMappings[j].Interactions[0].MixedRealityInputAction = currentMapping.MixedRealityInputAction; } else if (currentMapping.InputType == DeviceInputType.SpatialGrip) { thisProfile.MixedRealityControllerMappings[j].Interactions[1].MixedRealityInputAction = currentMapping.MixedRealityInputAction; } } serializedObject.Update(); controllerList.DeleteArrayElementAtIndex(i); EditorUtility.DisplayDialog("Mappings updated", "The \"HoloLens Voice and Clicker\" mappings have been migrated to a new serialization. Please save this asset.", "Okay, thanks!"); return; } } } #endregion Profile Migration if (!useCustomInteractionMappings) { bool skip = false; // Merge controllers with the same supported controller type. for (int j = 0; j < controllerRenderList.Count; j++) { if (controllerRenderList[j].SupportedControllerType == supportedControllerType && controllerRenderList[j].Handedness == handedness) { try { thisProfile.MixedRealityControllerMappings[i].SynchronizeInputActions(controllerRenderList[j].Interactions); } catch (ArgumentException e) { Debug.LogError($"Controller mappings between {thisProfile.MixedRealityControllerMappings[i].Description} and {controllerMapping.Description} do not match. Error message: {e.Message}"); } serializedObject.ApplyModifiedProperties(); skip = true; } } if (skip) { continue; } } controllerRenderList.Add(new ControllerRenderProfile(supportedControllerType, handedness, thisProfile.MixedRealityControllerMappings[i].Interactions)); string controllerTitle = thisProfile.MixedRealityControllerMappings[i].Description; var interactionsProperty = controllerMappingProperty.FindPropertyRelative("interactions"); if (useCustomInteractionMappings) { if (horizontalScope != null) { horizontalScope.Dispose(); horizontalScope = null; } GUILayout.Space(24f); using (var verticalScope = new GUILayout.VerticalScope()) { using (horizontalScope = new GUILayout.HorizontalScope()) { EditorGUILayout.LabelField(controllerTitle, EditorStyles.boldLabel); if (GUILayout.Button(ControllerMinusButtonContent, EditorStyles.miniButtonRight, GUILayout.Width(24f))) { controllerList.DeleteArrayElementAtIndex(i); return; } } EditorGUI.BeginChangeCheck(); // Generic Type dropdown Type[] genericTypes = MixedRealityControllerMappingProfile.CustomControllerMappingTypes; var genericTypeListContent = new GUIContent[genericTypes.Length]; var genericTypeListIds = new int[genericTypes.Length]; int currentGenericType = -1; for (int genericTypeIdx = 0; genericTypeIdx < genericTypes.Length; genericTypeIdx++) { var attribute = MixedRealityControllerAttribute.Find(genericTypes[genericTypeIdx]); if (attribute != null) { genericTypeListContent[genericTypeIdx] = new GUIContent(attribute.SupportedControllerType.ToString().Replace("Generic", "").ToProperCase() + " Controller"); } else { genericTypeListContent[genericTypeIdx] = new GUIContent("Unknown Controller"); } genericTypeListIds[genericTypeIdx] = genericTypeIdx; if (controllerType == genericTypes[genericTypeIdx]) { currentGenericType = genericTypeIdx; } } Debug.Assert(currentGenericType != -1); currentGenericType = EditorGUILayout.IntPopup(GenericTypeContent, currentGenericType, genericTypeListContent, genericTypeListIds); controllerType = genericTypes[currentGenericType]; { // Handedness dropdown var attribute = MixedRealityControllerAttribute.Find(controllerType); if (attribute != null && attribute.SupportedHandedness.Length >= 1) { // Make sure handedness is valid for the selected controller type. if (Array.IndexOf(attribute.SupportedHandedness, (Handedness)handednessProperty.intValue) < 0) { handednessProperty.intValue = (int)attribute.SupportedHandedness[0]; } if (attribute.SupportedHandedness.Length >= 2) { var handednessListContent = new GUIContent[attribute.SupportedHandedness.Length]; var handednessListIds = new int[attribute.SupportedHandedness.Length]; for (int handednessIdx = 0; handednessIdx < attribute.SupportedHandedness.Length; handednessIdx++) { handednessListContent[handednessIdx] = new GUIContent(attribute.SupportedHandedness[handednessIdx].ToString()); handednessListIds[handednessIdx] = (int)attribute.SupportedHandedness[handednessIdx]; } handednessProperty.intValue = EditorGUILayout.IntPopup(HandednessTypeContent, handednessProperty.intValue, handednessListContent, handednessListIds); } } else { handednessProperty.intValue = (int)Handedness.None; } } if (EditorGUI.EndChangeCheck()) { interactionsProperty.ClearArray(); serializedObject.ApplyModifiedProperties(); thisProfile.MixedRealityControllerMappings[i].ControllerType.Type = genericTypes[currentGenericType]; thisProfile.MixedRealityControllerMappings[i].SetDefaultInteractionMapping(true); serializedObject.ApplyModifiedProperties(); return; } if (InspectorUIUtility.RenderIndentedButton("Edit Input Action Map")) { ControllerPopupWindow.Show(controllerMapping, interactionsProperty, handedness); } if (InspectorUIUtility.RenderIndentedButton("Reset Input Actions")) { interactionsProperty.ClearArray(); serializedObject.ApplyModifiedProperties(); thisProfile.MixedRealityControllerMappings[i].SetDefaultInteractionMapping(true); serializedObject.ApplyModifiedProperties(); } } } else { if (handedness != Handedness.Right) { if (horizontalScope != null) { horizontalScope.Dispose(); horizontalScope = null; } horizontalScope = new GUILayout.HorizontalScope(); } var buttonContent = new GUIContent(controllerTitle, ControllerMappingLibrary.GetControllerTextureScaled(controllerType, handedness)); if (GUILayout.Button(buttonContent, MixedRealityStylesUtility.ControllerButtonStyle, GUILayout.Height(128f), GUILayout.MinWidth(32f), GUILayout.ExpandWidth(true))) { ControllerMappingSignature buttonSignature = new ControllerMappingSignature(supportedControllerType, handedness); ControllerPopupWindow.Show(controllerMapping, interactionsProperty, handedness, controllersAffectedByMappingSignatures[buttonSignature]); } } } if (horizontalScope != null) { horizontalScope.Dispose(); horizontalScope = null; } } } }
/// <summary> /// Displays the controller mapping window for the specified controller mapping /// </summary> /// <param name="controllerMapping"> The controller mapping being modified</param> /// <param name="interactionsList"> The underlying serialized property being modified</param> /// <param name="handedness"> The handedness of the controller </param> /// <param name="mappedControllers"> The list of controller types affected by this mapping</param> public static void Show(MixedRealityControllerMapping controllerMapping, SerializedProperty interactionsList, Handedness handedness = Handedness.None, List <string> mappedControllers = null) { if (window != null) { window.Close(); } window = null; if (!MixedRealityToolkit.IsInitialized) { throw new InvalidOperationException("Mixed Reality Toolkit hasn't been initialized yet! Open a scene with a Mixed Reality Toolkit to initialize it before editing the controller mappings."); } window = CreateInstance <ControllerPopupWindow>(); window.thisWindow = window; window.titleContent = new GUIContent($"{controllerMapping.Description} - Input Action Assignment"); window.mappedControllerList = mappedControllers; window.currentControllerMapping = controllerMapping; window.currentInteractionList = interactionsList; isMouseInRects = new bool[interactionsList.arraySize]; string editorWindowOptionsPath = ResolveEditorWindowOptionsPath(); if (!File.Exists(editorWindowOptionsPath)) { var empty = new ControllerInputActionOptions { Controllers = new List <ControllerInputActionOption> { new ControllerInputActionOption { Controller = 0, Handedness = Handedness.None, InputLabelPositions = new[] { new Vector2(0, 0) }, IsLabelFlipped = new [] { false } } } }; File.WriteAllText(editorWindowOptionsPath, JsonUtility.ToJson(empty, true)); AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); } else { controllerInputActionOptions = JsonUtility.FromJson <ControllerInputActionOptions>(File.ReadAllText(editorWindowOptionsPath)); if (controllerInputActionOptions.Controllers.Any(option => option.Controller == controllerMapping.SupportedControllerType && option.Handedness == handedness)) { window.currentControllerOption = controllerInputActionOptions.Controllers.FirstOrDefault(option => option.Controller == controllerMapping.SupportedControllerType && option.Handedness == handedness); if (window.currentControllerOption != null && window.currentControllerOption.IsLabelFlipped == null) { window.currentControllerOption.IsLabelFlipped = new bool[interactionsList.arraySize]; } } } var windowSize = new Vector2(controllerMapping.HasCustomInteractionMappings ? 896f : 768f, 512f); window.maxSize = windowSize; window.minSize = windowSize; window.CenterOnMainWin(); window.ShowUtility(); defaultLabelWidth = EditorGUIUtility.labelWidth; defaultFieldWidth = EditorGUIUtility.fieldWidth; }
/// <summary> /// Resets the input actions of the controller mapping according to the mapping's GetDefaultInteractionMappings() function /// </summary> /// <param name="controllerMapping">A reference to the controller mapping struct geing reset</param> private void ResetInputActions(ref MixedRealityControllerMapping controllerMapping) { controllerMapping.SetDefaultInteractionMapping(true); serializedObject.ApplyModifiedProperties(); ControllerPopupWindow.RepaintWindow(); }
/// <summary> /// Query the controller mapping library for any mappings for a specific controller /// </summary> /// <param name="mappings"></param> /// <param name="controllerType">Type of controller to search for</param> /// <param name="handedness">Specific controller hand to query</param> /// <param name="resolvedMapping">If found, the specific controller mapping is returned</param> /// <returns>Returns true if a mapping profile is found</returns> /// <remarks>Will also check for any controller mappings assigned to the handedness of Both (both hands) allowing a single configuration to be used for either hand</remarks> public static bool GetControllerInteractionMapping(this List <MixedRealityControllerMapping> mappings, Type controllerType, Handedness handedness, out MixedRealityControllerMapping resolvedMapping) { resolvedMapping = MixedRealityControllerMapping.None; for (int i = 0; i < mappings?.Count; i++) { // Assign any known interaction mappings. if (mappings[i].ControllerType?.Type == controllerType && (mappings[i].Handedness == handedness || mappings[i].Handedness == Handedness.Both) && mappings[i].Interactions.Length > 0) { resolvedMapping = mappings[i]; return(true); } } return(false); }