private static void RenderList(SerializedProperty list)
        {
            EditorGUILayout.Space();
            GUILayout.BeginVertical();

            if (MixedRealityEditorUtility.RenderIndentedButton(AddButtonContent, EditorStyles.miniButton))
            {
                list.arraySize += 1;
                var inputAction            = list.GetArrayElementAtIndex(list.arraySize - 1);
                var inputActionId          = inputAction.FindPropertyRelative("id");
                var inputActionDescription = inputAction.FindPropertyRelative("description");
                var inputActionConstraint  = inputAction.FindPropertyRelative("axisConstraint");
                inputActionConstraint.intValue     = 0;
                inputActionDescription.stringValue = $"New Action {inputActionId.intValue = list.arraySize}";
            }

            GUILayout.Space(12f);

            GUILayout.BeginVertical();

            GUILayout.BeginHorizontal();
            var labelWidth = EditorGUIUtility.labelWidth;

            EditorGUIUtility.labelWidth = 36f;
            EditorGUILayout.LabelField(ActionContent, GUILayout.ExpandWidth(true));
            EditorGUILayout.LabelField(AxisConstraintContent, GUILayout.Width(96f));
            EditorGUILayout.LabelField(string.Empty, GUILayout.Width(24f));
            EditorGUIUtility.labelWidth = labelWidth;
            GUILayout.EndHorizontal();

            scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);

            for (int i = 0; i < list.arraySize; i++)
            {
                EditorGUILayout.BeginHorizontal();
                var previousLabelWidth = EditorGUIUtility.labelWidth;
                EditorGUIUtility.labelWidth = 64f;
                SerializedProperty inputAction            = list.GetArrayElementAtIndex(i);
                SerializedProperty inputActionDescription = inputAction.FindPropertyRelative("description");
                var inputActionConstraint = inputAction.FindPropertyRelative("axisConstraint");
                EditorGUILayout.PropertyField(inputActionDescription, GUIContent.none);
                EditorGUILayout.PropertyField(inputActionConstraint, GUIContent.none, GUILayout.Width(96f));
                EditorGUIUtility.labelWidth = previousLabelWidth;

                if (GUILayout.Button(MinusButtonContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
                {
                    list.DeleteArrayElementAtIndex(i);
                }

                EditorGUILayout.EndHorizontal();
            }

            EditorGUILayout.EndScrollView();
            GUILayout.EndVertical();
            GUILayout.EndVertical();
        }
        /// <summary>
        /// Render the Mixed Reality Toolkit Logo.
        /// </summary>
        protected void RenderMRTKLogo()
        {
            // If we're being rendered as a sub profile, don't show the logo
            if (RenderAsSubProfile)
            {
                return;
            }

            MixedRealityEditorUtility.RenderMixedRealityToolkitLogo();
        }
        private void RenderProjectOptimizations()
        {
            GUILayout.BeginVertical("Box");
            EditorGUILayout.LabelField(this.ToolbarTitles[(int)ToolbarSection.Project], BoldLargeTitle);
            using (new EditorGUI.IndentLevelScope())
            {
                BuildSection("Single Pass Instanced Rendering", "https://docs.unity3d.com/Manual/SinglePassStereoRendering.html", () =>
                {
                    EditorGUILayout.LabelField("Single Pass Instanced Rendering is an option in the Unity render pipeline to more efficiently render your scene and optimize CPU & GPU work. This path requires shaders though to be written to support instancing which is automatic in all Unity & MRTK shaders. Click the \"Learn More\" button to learn how to update your custom shaders to support instancing.", EditorStyles.wordWrappedLabel);

                    EditorGUILayout.BeginHorizontal();
                    singlePassInstanced = EditorGUILayout.ToggleLeft("Set Single Pass Instanced Rendering", singlePassInstanced);
                    if (GUILayout.Button(new GUIContent("Learn More", "Learn more about Single Pass Instanced Rendering"), EditorStyles.miniButton, GUILayout.Width(120f)))
                    {
                        Application.OpenURL("https://docs.unity3d.com/Manual/SinglePassInstancing.html");
                    }
                    EditorGUILayout.EndHorizontal();
                });

                // TODO: Put in Quality settings section

                BuildSection("Depth Buffer Sharing", null, () =>
                {
                    EditorGUILayout.LabelField("This option shares the application's depth buffer with the running platform which allows the platform to more accurately stabilize holograms and content.", EditorStyles.wordWrappedLabel);
                    EditorGUILayout.LabelField("Note: Depth buffer sharing & format is a platform dependent feature.", EditorStyles.wordWrappedLabel);
                    enableDepthBufferSharing = EditorGUILayout.ToggleLeft("Enable Depth Buffer Sharing", enableDepthBufferSharing);

                    /*
                     * TODO: Finish rationalizing this section per platform
                     * EditorGUILayout.LabelField("The depth format determines the level of precision for the depth texture (i.e 16-bit vs 24-bit etc). Using a lower precision format (i.e 16-bit) will generally result in performance gains however this also results in lower precision to resolve one object being farther than another. Thus, particularly for AR devices, it is recommended to lower the camera's far plane value so there is a reduced range of possible values.", EditorStyles.wordWrappedLabel);
                     * enableDepthBufferSharing = EditorGUILayout.BeginToggleGroup("Enable Depth Buffer Sharing", enableDepthBufferSharing);
                     *
                     * if (this.PerfTarget == PerformanceTarget.AR_Headsets)
                     * {
                     *  enable16BitDepthBuffer = EditorGUILayout.ToggleLeft("Set Depth Buffer to 16-bit", enable16BitDepthBuffer);
                     *  enable16BitDepthBuffer = EditorGUILayout.ToggleLeft("Set MRTK Camera Far Plane to 50m", enable16BitDepthBuffer);
                     * }
                     * else if (this.PerfTarget == PerformanceTarget.VR_Standalone)
                     * {
                     *  enable16BitDepthBuffer = EditorGUILayout.ToggleLeft("Set Depth Buffer to 16-bit", enable16BitDepthBuffer);
                     *  enable16BitDepthBuffer = EditorGUILayout.ToggleLeft("Set MRTK Camera Far Plane to 50m", enable16BitDepthBuffer);
                     * }
                     * EditorGUILayout.EndToggleGroup();
                     */
                });

                if (MixedRealityEditorUtility.RenderIndentedButton("Optimize Project"))
                {
                    OptimizeProject();
                }
            }

            GUILayout.EndVertical();
        }
        public override void OnInspectorGUI()
        {
            RenderProfileHeader(ProfileTitle, ProfileDescription, target, true, BackProfileType.Input);

            using (new GUIEnabledWrapper(!IsProfileLock((BaseMixedRealityProfile)target)))
            {
                serializedObject.Update();
                currentlySelectedPointerOption = -1;

                EditorGUILayout.Space();
                EditorGUILayout.LabelField("Gaze Settings", EditorStyles.boldLabel);
                {
                    EditorGUILayout.Space();
                    EditorGUILayout.PropertyField(gazeCursorPrefab);
                    EditorGUILayout.PropertyField(gazeProviderType);
                    EditorGUILayout.Space();

                    if (MixedRealityEditorUtility.RenderIndentedButton("Customize Gaze Provider Settings"))
                    {
                        Selection.activeObject = CameraCache.Main.gameObject;
                    }
                }

                EditorGUILayout.Space();
                EditorGUILayout.LabelField("Pointer Settings", EditorStyles.boldLabel);
                {
                    EditorGUILayout.PropertyField(pointingExtent);
                    EditorGUILayout.PropertyField(pointingRaycastLayerMasks, true);
                    EditorGUILayout.PropertyField(pointerMediator);

                    EditorGUILayout.Space();
                    showPointerOptionProperties = EditorGUILayout.Foldout(showPointerOptionProperties, "Pointer Options", true);
                    if (showPointerOptionProperties)
                    {
                        using (new EditorGUI.IndentLevelScope())
                        {
                            pointerOptionList.DoLayoutList();
                        }
                    }
                }

                EditorGUILayout.Space();
                EditorGUILayout.LabelField("Debug Settings", EditorStyles.boldLabel);
                {
                    EditorGUILayout.PropertyField(debugDrawPointingRays);
                    EditorGUILayout.PropertyField(debugDrawPointingRayColors, true);
                }

                serializedObject.ApplyModifiedProperties();
            }
        }
        private void OnGUI()
        {
            windowScrollPosition = EditorGUILayout.BeginScrollView(windowScrollPosition);

            MixedRealityEditorUtility.RenderMixedRealityToolkitLogo();

            // Render Title
            EditorGUILayout.LabelField("Mixed Reality Toolkit Optimize Window", BoldLargeTitle);
            EditorGUILayout.LabelField("This tool automates the process of updating your project, currently open scene, and material assets to recommended settings for Mixed Reality", EditorStyles.wordWrappedLabel);
            EditorGUILayout.Space();

            PerfTarget = (PerformanceTarget)EditorGUILayout.Popup("Performance Target", (int)this.PerfTarget, PerformanceTargetEnums);
            EditorGUILayout.HelpBox(PerformanceTargetDescriptions[(int)PerfTarget], MessageType.Info);
            EditorGUILayout.Space();

            if (!PlayerSettings.virtualRealitySupported)
            {
                EditorGUILayout.HelpBox("Current build target does not have virtual reality support enabled", MessageType.Warning);
            }

            selectedToolbarIndex = GUILayout.Toolbar(selectedToolbarIndex, ToolbarTitles);
            if (selectedToolbarIndex == 0)
            {
                RenderProjectOptimizations();
            }
            else if (selectedToolbarIndex == 1)
            {
                RenderSceneOptimizations();
            }
            else
            {
                RenderShaderOptimizations();
            }

            EditorGUILayout.EndScrollView();
        }
        private void RenderList(SerializedProperty list)
        {
            bool changed = false;

            using (new EditorGUILayout.VerticalScope())
            {
                if (MixedRealityEditorUtility.RenderIndentedButton(AddObserverContent, EditorStyles.miniButton))
                {
                    list.InsertArrayElementAtIndex(list.arraySize);
                    SerializedProperty observer = list.GetArrayElementAtIndex(list.arraySize - 1);

                    SerializedProperty observerName = observer.FindPropertyRelative("componentName");
                    observerName.stringValue = $"New spatial observer {list.arraySize - 1}";

                    SerializedProperty runtimePlatform = observer.FindPropertyRelative("runtimePlatform");
                    runtimePlatform.intValue = -1;

                    SerializedProperty configurationProfile = observer.FindPropertyRelative("observerProfile");
                    configurationProfile.objectReferenceValue = null;

                    serializedObject.ApplyModifiedProperties();

                    SystemType observerType = ((MixedRealitySpatialAwarenessSystemProfile)serializedObject.targetObject).ObserverConfigurations[list.arraySize - 1].ComponentType;
                    observerType.Type = null;

                    observerFoldouts = new bool[list.arraySize];
                    return;
                }

                if (list == null || list.arraySize == 0)
                {
                    EditorGUILayout.HelpBox("The Mixed Reality Spatial Awareness System requires one or more observers.", MessageType.Warning);
                    return;
                }

                for (int i = 0; i < list.arraySize; i++)
                {
                    SerializedProperty observer        = list.GetArrayElementAtIndex(i);
                    SerializedProperty observerName    = observer.FindPropertyRelative("componentName");
                    SerializedProperty observerType    = observer.FindPropertyRelative("componentType");
                    SerializedProperty observerProfile = observer.FindPropertyRelative("observerProfile");
                    SerializedProperty runtimePlatform = observer.FindPropertyRelative("runtimePlatform");

                    using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
                    {
                        using (new EditorGUILayout.HorizontalScope())
                        {
                            observerFoldouts[i] = EditorGUILayout.Foldout(observerFoldouts[i], observerName.stringValue, true);

                            if (GUILayout.Button(RemoveObserverContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
                            {
                                list.DeleteArrayElementAtIndex(i);
                                serializedObject.ApplyModifiedProperties();
                                changed = true;
                                break;
                            }
                        }

                        if (observerFoldouts[i])
                        {
                            EditorGUI.BeginChangeCheck();
                            EditorGUILayout.PropertyField(observerType, ComponentTypeContent);
                            if (EditorGUI.EndChangeCheck())
                            {
                                serializedObject.ApplyModifiedProperties();
                                System.Type type = ((MixedRealitySpatialAwarenessSystemProfile)serializedObject.targetObject).ObserverConfigurations[i].ComponentType.Type;
                                ApplyObserverConfiguration(type, observerName, observerProfile, runtimePlatform);
                                break;
                            }

                            EditorGUI.BeginChangeCheck();
                            EditorGUILayout.PropertyField(runtimePlatform, RuntimePlatformContent);
                            changed |= EditorGUI.EndChangeCheck();

                            System.Type serviceType = null;
                            if (observerProfile.objectReferenceValue != null)
                            {
                                serviceType = (target as MixedRealitySpatialAwarenessSystemProfile).ObserverConfigurations[i].ComponentType;
                            }

                            changed |= RenderProfile(observerProfile, null, true, false, serviceType);

                            serializedObject.ApplyModifiedProperties();
                        }
                    }
                }

                if (changed && MixedRealityToolkit.IsInitialized)
                {
                    EditorApplication.delayCall += () => MixedRealityToolkit.Instance.ResetConfiguration(MixedRealityToolkit.Instance.ActiveProfile);
                }
            }
        }
Esempio n. 7
0
        private void RenderControllerList(SerializedProperty controllerList)
        {
            if (thisProfile.ControllerVisualizationSettings.Length != controllerList.arraySize)
            {
                return;
            }

            EditorGUILayout.Space();

            if (MixedRealityEditorUtility.RenderIndentedButton(ControllerAddButtonContent, EditorStyles.miniButton))
            {
                controllerList.InsertArrayElementAtIndex(controllerList.arraySize);
                var index             = controllerList.arraySize - 1;
                var controllerSetting = controllerList.GetArrayElementAtIndex(index);
                var mixedRealityControllerMappingDescription = controllerSetting.FindPropertyRelative("description");
                mixedRealityControllerMappingDescription.stringValue = typeof(GenericJoystickController).Name;
                var mixedRealityControllerHandedness = controllerSetting.FindPropertyRelative("handedness");
                mixedRealityControllerHandedness.intValue = 1;
                serializedObject.ApplyModifiedProperties();
                thisProfile.ControllerVisualizationSettings[index].ControllerType.Type = typeof(GenericJoystickController);
                return;
            }

            for (int i = 0; i < controllerList.arraySize; i++)
            {
                EditorGUILayout.Space();
                EditorGUILayout.BeginHorizontal();

                var  controllerSetting = controllerList.GetArrayElementAtIndex(i);
                var  mixedRealityControllerMappingDescription = controllerSetting.FindPropertyRelative("description");
                bool hasValidType = thisProfile.ControllerVisualizationSettings[i].ControllerType != null &&
                                    thisProfile.ControllerVisualizationSettings[i].ControllerType.Type != null;

                mixedRealityControllerMappingDescription.stringValue = hasValidType
                    ? thisProfile.ControllerVisualizationSettings[i].ControllerType.Type.Name.ToProperCase()
                    : "Undefined Controller";

                serializedObject.ApplyModifiedProperties();
                var mixedRealityControllerHandedness = controllerSetting.FindPropertyRelative("handedness");
                EditorGUILayout.LabelField($"{mixedRealityControllerMappingDescription.stringValue} {((Handedness)mixedRealityControllerHandedness.intValue).ToString().ToProperCase()} Hand", EditorStyles.boldLabel);

                if (GUILayout.Button(ControllerMinusButtonContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
                {
                    controllerList.DeleteArrayElementAtIndex(i);
                    EditorGUILayout.EndHorizontal();
                    GUILayout.EndVertical();
                    return;
                }

                EditorGUILayout.EndHorizontal();

                EditorGUILayout.PropertyField(controllerSetting.FindPropertyRelative("controllerType"));
                EditorGUILayout.PropertyField(controllerSetting.FindPropertyRelative("controllerVisualizationType"));

                if (!hasValidType)
                {
                    EditorGUILayout.HelpBox("A controller type must be defined!", MessageType.Error);
                }

                var handednessValue = mixedRealityControllerHandedness.intValue - 1;

                // Reset in case it was set to something other than left or right.
                if (handednessValue < 0 || handednessValue > 1)
                {
                    handednessValue = 0;
                }

                EditorGUI.BeginChangeCheck();
                handednessValue = EditorGUILayout.IntPopup(new GUIContent(mixedRealityControllerHandedness.displayName, mixedRealityControllerHandedness.tooltip), handednessValue, HandednessSelections, null);

                if (EditorGUI.EndChangeCheck())
                {
                    mixedRealityControllerHandedness.intValue = handednessValue + 1;
                }

                var overrideModel       = controllerSetting.FindPropertyRelative("overrideModel");
                var overrideModelPrefab = overrideModel.objectReferenceValue as GameObject;

                var controllerUseDefaultModelOverride = controllerSetting.FindPropertyRelative("useDefaultModel");
                EditorGUILayout.PropertyField(controllerUseDefaultModelOverride);

                if (controllerUseDefaultModelOverride.boolValue && overrideModelPrefab != null)
                {
                    EditorGUILayout.HelpBox("When default model is used, the override model will only be used if the default model cannot be loaded from the driver.", MessageType.Warning);
                }

                EditorGUI.BeginChangeCheck();
                overrideModelPrefab = EditorGUILayout.ObjectField(new GUIContent(overrideModel.displayName, "If no override model is set, the global model is used."), overrideModelPrefab, typeof(GameObject), false) as GameObject;

                if (EditorGUI.EndChangeCheck() && CheckVisualizer(overrideModelPrefab))
                {
                    overrideModel.objectReferenceValue = overrideModelPrefab;
                }
            }
        }
Esempio n. 8
0
        private void RenderControllerList(SerializedProperty controllerList)
        {
            if (thisProfile.MixedRealityControllerMappingProfiles.Length != controllerList.arraySize)
            {
                return;
            }

            if (MixedRealityEditorUtility.RenderIndentedButton(ControllerAddButtonContent, EditorStyles.miniButton))
            {
                AddController(controllerList, typeof(GenericJoystickController));
                return;
            }

            controllerRenderList.Clear();

            showControllerDefinitions = EditorGUILayout.Foldout(showControllerDefinitions, "Controller Definitions");
            if (showControllerDefinitions)
            {
                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)
                                {
                                    try
                                    {
                                        thisProfile.MixedRealityControllerMappingProfiles[i].SynchronizeInputActions(controllerRenderList[j].Interactions);
                                    }
                                    catch (ArgumentException e)
                                    {
                                        Debug.LogError($"Controller mappings between {thisProfile.MixedRealityControllerMappingProfiles[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.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())
                                {
                                    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.MixedRealityControllerMappingProfiles[i].ControllerType.Type = genericTypes[currentGenericType];
                                    thisProfile.MixedRealityControllerMappingProfiles[i].SetDefaultInteractionMapping(true);
                                    serializedObject.ApplyModifiedProperties();
                                    return;
                                }

                                if (MixedRealityEditorUtility.RenderIndentedButton("Edit Input Action Map"))
                                {
                                    ControllerPopupWindow.Show(controllerMapping, interactionsProperty, handedness);
                                }

                                if (MixedRealityEditorUtility.RenderIndentedButton("Reset Input Actions"))
                                {
                                    interactionsProperty.ClearArray();
                                    serializedObject.ApplyModifiedProperties();
                                    thisProfile.MixedRealityControllerMappingProfiles[i].SetDefaultInteractionMapping(true);
                                    serializedObject.ApplyModifiedProperties();
                                }
                            }
                        }
                        else
                        {
                            if (supportedControllerType == SupportedControllerType.WindowsMixedReality &&
                                handedness == Handedness.None)
                            {
                                controllerTitle = "HoloLens Voice and Clicker";
                            }

                            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)))
                            {
                                ControllerPopupWindow.Show(controllerMapping, interactionsProperty, handedness);
                            }
                        }
                    }

                    if (horizontalScope != null)
                    {
                        horizontalScope.Dispose(); horizontalScope = null;
                    }
                }
            }
        }
Esempio n. 9
0
        public override void OnInspectorGUI()
        {
            RenderProfileHeader(ProfileTitle, ProfileDescription, target, isInitialized, BackProfileType.Input);

            RenderMixedRealityInputConfigured();

            if (!MixedRealityInspectorUtility.CheckMixedRealityConfigured(false))
            {
                return;
            }

            if (MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile == null)
            {
                EditorGUILayout.HelpBox("No Input Actions profile was specified.", MessageType.Error);
            }

            using (new GUIEnabledWrapper(!IsProfileLock((BaseMixedRealityProfile)target), false))
            {
                serializedObject.Update();

                selectedBaseActionId = RenderBaseInputAction(selectedBaseActionId, out currentBaseAction);

                using (new GUIEnabledWrapper(currentBaseAction != MixedRealityInputAction.None, false))
                {
                    RenderCriteriaField(currentBaseAction);

                    if (selectedBaseActionId == selectedRuleActionId)
                    {
                        selectedRuleActionId = 0;
                    }

                    selectedRuleActionId = RenderRuleInputAction(selectedRuleActionId, out currentRuleAction);

                    EditorGUILayout.Space();
                }

                bool addButtonEnable = !RuleExists() &&
                                       currentBaseAction != MixedRealityInputAction.None &&
                                       currentRuleAction != MixedRealityInputAction.None &&
                                       currentBaseAction.AxisConstraint != AxisType.None &&
                                       currentBaseAction.AxisConstraint != AxisType.Raw;

                using (new GUIEnabledWrapper(addButtonEnable, false))
                {
                    if (MixedRealityEditorUtility.RenderIndentedButton(RuleAddButtonContent, EditorStyles.miniButton))
                    {
                        AddRule();
                        ResetCriteria();
                    }
                }

                EditorGUILayout.Space();

                var isWideMode = EditorGUIUtility.wideMode;
                EditorGUIUtility.wideMode = true;

                RenderList(inputActionRulesDigital, digitalFoldouts);
                RenderList(inputActionRulesSingleAxis, singleAxisFoldouts);
                RenderList(inputActionRulesDualAxis, dualAxisFoldouts);
                RenderList(inputActionRulesVectorAxis, vectorFoldouts);
                RenderList(inputActionRulesQuaternionAxis, quaternionFoldouts);
                RenderList(inputActionRulesPoseAxis, poseFoldouts);

                EditorGUIUtility.wideMode = isWideMode;
                serializedObject.ApplyModifiedProperties();
            }
        }
        /// <summary>
        /// Helper function to render header correctly for all profiles
        /// </summary>
        /// <param name="title">Title of profile</param>
        /// <param name="description">profile tooltip describing purpose</param>
        /// <param name="selectionObject">The profile object. Used to re-select the object after MRTK instance is created.</param>
        /// <param name="isProfileInitialized">profile properties are full initialized for rendering</param>
        /// <param name="backText">Text for back button if not rendering as sub-profile</param>
        /// <param name="backProfile">Target profile to return to if not rendering as sub-profile</param>
        protected void RenderProfileHeader(string title, string description, Object selectionObject, bool isProfileInitialized = true, BackProfileType returnProfileTarget = BackProfileType.Configuration)
        {
            RenderMRTKLogo();

            var profile = target as BaseMixedRealityProfile;

            if (!RenderAsSubProfile)
            {
                if (!profile.IsCustomProfile)
                {
                    EditorGUILayout.HelpBox("Default MRTK profiles cannot be edited. Create a clone of this profile to modify settings.", MessageType.Warning);
                    if (MixedRealityEditorUtility.RenderIndentedButton(new GUIContent("Clone"), EditorStyles.miniButton))
                    {
                        MixedRealityProfileCloneWindow.OpenWindow(null, (BaseMixedRealityProfile)target, null);
                    }
                }

                if (!isProfileInitialized)
                {
                    EditorGUILayout.HelpBox("This profile is not assigned to an active MRTK instance in any of your scenes. Some properties may not be visible", MessageType.Error);

                    if (!MixedRealityToolkit.IsInitialized)
                    {
                        if (MixedRealityEditorUtility.RenderIndentedButton(new GUIContent("Add Mixed Reality Toolkit instance to scene"), EditorStyles.miniButton))
                        {
                            MixedRealityInspectorUtility.AddMixedRealityToolkitToScene(MixedRealityInspectorUtility.GetDefaultConfigProfile());
                            // After the toolkit has been created, set the selection back to this item so the user doesn't get lost
                            Selection.activeObject = selectionObject;
                        }
                    }
                }
            }
            else
            {
                if (!isProfileInitialized && profile.IsCustomProfile)
                {
                    EditorGUILayout.HelpBox("Some properties may not be editable in this profile. Please refer to the error messages below to resolve editing.", MessageType.Warning);
                }

                if (MixedRealityToolkit.IsInitialized)
                {
                    if (IsProfileInActiveInstance())
                    {
                        DrawBacktrackProfileButton(returnProfileTarget);
                    }
                    else if (!isProfileInitialized)
                    {
                        EditorGUILayout.HelpBox("This profile is not assigned to an active MRTK instance in any of your scenes. Some properties may not be editable", MessageType.Error);

                        if (!MixedRealityToolkit.IsInitialized)
                        {
                            if (MixedRealityEditorUtility.RenderIndentedButton(new GUIContent("Add Mixed Reality Toolkit instance to scene"), EditorStyles.miniButton))
                            {
                                MixedRealityInspectorUtility.AddMixedRealityToolkitToScene(MixedRealityInspectorUtility.GetDefaultConfigProfile());
                                // After the toolkit has been created, set the selection back to this item so the user doesn't get lost
                                Selection.activeObject = selectionObject;
                            }
                        }
                    }
                }
            }

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(title, description), EditorStyles.boldLabel, GUILayout.ExpandWidth(true));
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.LabelField(string.Empty, GUI.skin.horizontalSlider);
        }
        public override void OnInspectorGUI()
        {
            var configurationProfile = (MixedRealityToolkitConfigurationProfile)target;

            serializedObject.Update();

            RenderMRTKLogo();

            if (!MixedRealityToolkit.IsInitialized)
            {
                EditorGUILayout.HelpBox("No Mixed Reality Toolkit found in scene.", MessageType.Warning);
                if (MixedRealityEditorUtility.RenderIndentedButton("Add Mixed Reality Toolkit instance to scene"))
                {
                    MixedRealityInspectorUtility.AddMixedRealityToolkitToScene(configurationProfile);
                }
            }

            if (!configurationProfile.IsCustomProfile)
            {
                EditorGUILayout.HelpBox("The Mixed Reality Toolkit's core SDK profiles can be used to get up and running quickly.\n\n" +
                                        "You can use the default profiles provided, copy and customize the default profiles, or create your own.", MessageType.Warning);
                EditorGUILayout.BeginHorizontal();

                if (GUILayout.Button("Copy & Customize"))
                {
                    var originalSelection = Selection.activeObject;
                    CreateCustomProfile(target as BaseMixedRealityProfile);
                    Selection.activeObject = originalSelection;
                }

                if (MixedRealityToolkit.IsInitialized)
                {
                    if (GUILayout.Button("Create new profiles"))
                    {
                        ScriptableObject profile = CreateInstance(nameof(MixedRealityToolkitConfigurationProfile));
                        var newProfile           = profile.CreateAsset("Assets/MixedRealityToolkit.Generated/CustomProfiles") as MixedRealityToolkitConfigurationProfile;
                        UnityEditor.Undo.RecordObject(MixedRealityToolkit.Instance, "Create new profiles");
                        MixedRealityToolkit.Instance.ActiveProfile = newProfile;
                        Selection.activeObject = newProfile;
                    }
                }

                EditorGUILayout.EndHorizontal();
                EditorGUILayout.LabelField(string.Empty, GUI.skin.horizontalSlider);
            }

            bool isGUIEnabled = !IsProfileLock((BaseMixedRealityProfile)target);

            GUI.enabled = isGUIEnabled;

            EditorGUI.BeginChangeCheck();
            bool changed = false;

            // Experience configuration
            ExperienceScale experienceScale = (ExperienceScale)targetExperienceScale.intValue;

            EditorGUILayout.PropertyField(targetExperienceScale, TargetScaleContent);

            string scaleDescription = GetExperienceDescription(experienceScale);

            if (!string.IsNullOrEmpty(scaleDescription))
            {
                EditorGUILayout.HelpBox(scaleDescription, MessageType.Info);
                EditorGUILayout.Space();
            }

            EditorGUILayout.BeginHorizontal();

            EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Width(100));
            GUI.enabled        = true; // Force enable so we can view profile defaults
            SelectedProfileTab = GUILayout.SelectionGrid(SelectedProfileTab, ProfileTabTitles, 1, EditorStyles.boldLabel, GUILayout.MaxWidth(125));
            GUI.enabled        = isGUIEnabled;
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
            using (new EditorGUI.IndentLevelScope())
            {
                changed |= RenderProfileFuncs[SelectedProfileTab]();
            }
            EditorGUILayout.EndVertical();
            EditorGUILayout.EndHorizontal();

            if (!changed)
            {
                changed |= EditorGUI.EndChangeCheck();
            }

            serializedObject.ApplyModifiedProperties();
            GUI.enabled = true;

            if (changed && MixedRealityToolkit.IsInitialized)
            {
                EditorApplication.delayCall += () => MixedRealityToolkit.Instance.ResetConfiguration(configurationProfile);
            }
        }
        private void RenderSceneOptimizations()
        {
            GUILayout.BeginVertical("Box");
            EditorGUILayout.LabelField(this.ToolbarTitles[(int)ToolbarSection.Scene], BoldLargeTitle);
            using (new EditorGUI.IndentLevelScope())
            {
                EditorGUILayout.LabelField("This section provides controls and performance information for the currently opened scene. Any optimizations performed are only for the active scene at any moment. Also be aware that changes made while in editor Play mode will not be saved.", EditorStyles.wordWrappedLabel);

                BuildSection("Lighting Settings", "https://docs.unity3d.com/Manual/GlobalIllumination.html", () =>
                {
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.LabelField("Global Illumination can produce great visual results but sometimes at great expense.", EditorStyles.wordWrappedLabel);
                    if (GUILayout.Button(new GUIContent("View Lighting Settings", "Open Lighting Settings"), EditorStyles.miniButton, GUILayout.Width(160f)))
                    {
                        EditorApplication.ExecuteMenuItem("Window/Rendering/Lighting Settings");
                    }
                    EditorGUILayout.EndHorizontal();

                    disableRealtimeGlobalIllumination = EditorGUILayout.ToggleLeft("Disable Realtime Global Illumination", disableRealtimeGlobalIllumination);

                    if (this.PerfTarget == PerformanceTarget.AR_Headsets)
                    {
                        disableBakedGlobalIllumination = EditorGUILayout.ToggleLeft("Disable Baked Global Illumination", disableBakedGlobalIllumination);
                    }

                    if (MixedRealityEditorUtility.RenderIndentedButton("Optimize Lighting"))
                    {
                        OptimizeScene();
                    }
                });

                BuildSection("Live Scene Analysis", null, () =>
                {
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.LabelField(lastAnalyzedTime == null ? "Click analysis button for MRTK to scan your currently opened scene" : "Scanned " + GetRelativeTime(lastAnalyzedTime));

                    if (GUILayout.Button("Analyze Scene", GUILayout.Width(160f)))
                    {
                        AnalyzeScene();
                        lastAnalyzedTime = DateTime.UtcNow;
                    }
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.Space();

                    if (lastAnalyzedTime != null)
                    {
                        // Lighting analysis
                        bool showNumOfSceneLights = this.sceneLights != null && this.sceneLights.Length > SceneLightCountMax[(int)PerfTarget];
                        bool showDisableShadows   = this.PerfTarget == PerformanceTarget.AR_Headsets;
                        if (showNumOfSceneLights || showDisableShadows)
                        {
                            EditorGUILayout.LabelField("Lighting Analysis", EditorStyles.boldLabel);
                            if (showNumOfSceneLights)
                            {
                                EditorGUILayout.LabelField(this.sceneLights.Length + " lights in the scene. Consider reducing the number of lights.");
                            }

                            if (showDisableShadows)
                            {
                                foreach (var l in this.sceneLights)
                                {
                                    if (l.shadows != LightShadows.None)
                                    {
                                        EditorGUILayout.ObjectField("Disable shadows", l, typeof(Light), true);
                                    }
                                }
                            }
                            EditorGUILayout.Space();
                        }

                        // Mesh Analysis
                        EditorGUILayout.LabelField("Polygon Count Analysis", EditorStyles.boldLabel);

                        EditorGUILayout.BeginHorizontal();
                        EditorGUILayout.LabelField(TotalPolyCountStr);
                        EditorGUILayout.LabelField(TotalActivePolyCountStr);
                        EditorGUILayout.LabelField(TotalInactivePolyCountStr);
                        EditorGUILayout.EndHorizontal();

                        EditorGUILayout.LabelField("Top " + TopListSize + " GameObjects in scene with highest polygon count");
                        for (int i = 0; i < LargestMeshes.Length; i++)
                        {
                            if (LargestMeshes[i] != null)
                            {
                                EditorGUILayout.BeginHorizontal();
                                EditorGUILayout.LabelField("Num of Polygons: " + this.LargestMeshSizes[i].ToString("N0"));
                                EditorGUILayout.ObjectField(this.LargestMeshes[i], typeof(GameObject), true);
                                if (GUILayout.Button(new GUIContent("View", "Selects & view this asset in inspector"), EditorStyles.miniButton, GUILayout.Width(42f)))
                                {
                                    Selection.activeObject = this.LargestMeshes[i];
                                }
                                EditorGUILayout.EndHorizontal();
                            }
                        }
                    }
                });
            }

            GUILayout.EndVertical();
        }
        private void RenderShaderOptimizations()
        {
            GUILayout.BeginVertical("Box");
            EditorGUILayout.LabelField(this.ToolbarTitles[(int)ToolbarSection.Shader], BoldLargeTitle);
            using (new EditorGUI.IndentLevelScope())
            {
                EditorGUILayout.LabelField("The Unity standard shader is generally not performant or optimized for Mixed Reality development. The MRTK Standard shader can be a more performant option. "
                                           + "This tool allows developers to discover and automatically convert materials in their project to the replacement shader option below. It is recommended to utilize the MRTK Standard Shader."
                                           , EditorStyles.wordWrappedLabel);
                EditorGUILayout.Space();

                replacementShader = (Shader)EditorGUILayout.ObjectField("Replacement Shader", replacementShader, typeof(Shader), false);
                if (replacementShader == null)
                {
                    EditorGUILayout.HelpBox("Please set a replacement shader to utilize this tool", MessageType.Error);
                    if (MixedRealityEditorUtility.RenderIndentedButton(new GUIContent("Use MRTK Standard Shader", "Set Replacement Shader to MRKT Standard Shader"), EditorStyles.miniButton, GUILayout.Width(200f)))
                    {
                        FindShaders();
                    }
                }
                else
                {
                    onlyUnityShader = EditorGUILayout.ToggleLeft("Only Discover Materials with Unity Standard Shader", onlyUnityShader);
                    EditorGUILayout.Space();

                    EditorGUILayout.BeginHorizontal();
                    if (GUILayout.Button("Discover Materials in Assets"))
                    {
                        DiscoverMaterials();
                    }

                    bool wasGUIEnabled = GUI.enabled;
                    GUI.enabled = wasGUIEnabled && discoveredMaterials.Count > 0;
                    if (GUILayout.Button("Convert All Discovered Materials"))
                    {
                        ConvertMaterials();
                    }
                    GUI.enabled = wasGUIEnabled;
                    EditorGUILayout.EndHorizontal();

                    if (discoveredMaterials.Count > 0)
                    {
                        showDiscoveredMaterials = EditorGUILayout.Foldout(showDiscoveredMaterials, "Discovered Materials", true);
                        if (showDiscoveredMaterials)
                        {
                            using (new EditorGUI.IndentLevelScope())
                            {
                                EditorGUILayout.LabelField("Discovered " + discoveredMaterials.Count + " materials to convert");

                                EditorGUILayout.BeginHorizontal();
                                EditorGUILayout.LabelField("Current Shader", EditorStyles.boldLabel);
                                EditorGUILayout.LabelField("Material Asset", EditorStyles.boldLabel);
                                EditorGUILayout.EndHorizontal();

                                for (int i = 0; i < discoveredMaterials.Count; i++)
                                {
                                    if (discoveredMaterials[i] != null)
                                    {
                                        EditorGUILayout.BeginHorizontal();
                                        EditorGUILayout.LabelField(discoveredMaterials[i].shader.name);
                                        discoveredMaterials[i] = (Material)EditorGUILayout.ObjectField(discoveredMaterials[i], typeof(Material), false);
                                        if (GUILayout.Button(new GUIContent("View", "Selects & views this asset in inspector"), EditorStyles.miniButton, GUILayout.Width(42f)))
                                        {
                                            Selection.activeObject = this.discoveredMaterials[i];
                                        }
                                        if (GUILayout.Button(new GUIContent("Convert", "Converts this material's shader to the replacement shader"), EditorStyles.miniButton, GUILayout.Width(64f)))
                                        {
                                            Undo.RecordObject(this.discoveredMaterials[i], "Convert to MRTK Standard shader");
                                            ConvertMaterial(this.discoveredMaterials[i]);
                                        }
                                        EditorGUILayout.EndHorizontal();
                                    }
                                }
                            }
                        }
                    }
                }
            }
            GUILayout.EndVertical();
            EditorGUILayout.Space();
        }
        private void RenderList(SerializedProperty list)
        {
            // Disable gestures list if we could not initialize successfully
            using (new GUIEnabledWrapper(isInitialized, false))
            {
                EditorGUILayout.Space();
                GUILayout.BeginVertical();

                if (MixedRealityEditorUtility.RenderIndentedButton(AddButtonContent, EditorStyles.miniButton))
                {
                    list.arraySize += 1;
                    var speechCommand = list.GetArrayElementAtIndex(list.arraySize - 1);
                    var keyword       = speechCommand.FindPropertyRelative("description");
                    keyword.stringValue = string.Empty;
                    var gestureType = speechCommand.FindPropertyRelative("gestureType");
                    gestureType.intValue = (int)GestureInputType.None;
                    var action   = speechCommand.FindPropertyRelative("action");
                    var actionId = action.FindPropertyRelative("id");
                    actionId.intValue = 0;
                    var actionDescription = action.FindPropertyRelative("description");
                    actionDescription.stringValue = string.Empty;
                    var actionConstraint = action.FindPropertyRelative("axisConstraint");
                    actionConstraint.intValue = 0;
                }

                if (list == null || list.arraySize == 0)
                {
                    EditorGUILayout.HelpBox("Define a new Gesture.", MessageType.Warning);
                    GUILayout.EndVertical();
                    UpdateGestureLabels();
                    return;
                }

                GUILayout.BeginVertical();

                GUILayout.BeginHorizontal();
                var labelWidth = EditorGUIUtility.labelWidth;
                EditorGUIUtility.labelWidth = 24f;
                EditorGUILayout.LabelField(DescriptionContent, GUILayout.ExpandWidth(true));
                EditorGUILayout.LabelField(GestureTypeContent, GUILayout.Width(80f));
                EditorGUILayout.LabelField(ActionContent, GUILayout.Width(64f));
                EditorGUILayout.LabelField(string.Empty, GUILayout.Width(24f));
                EditorGUIUtility.labelWidth = labelWidth;
                GUILayout.EndHorizontal();

                for (int i = 0; i < list.arraySize; i++)
                {
                    EditorGUILayout.BeginHorizontal();
                    SerializedProperty gesture = list.GetArrayElementAtIndex(i);
                    var keyword           = gesture.FindPropertyRelative("description");
                    var gestureType       = gesture.FindPropertyRelative("gestureType");
                    var action            = gesture.FindPropertyRelative("action");
                    var actionId          = action.FindPropertyRelative("id");
                    var actionDescription = action.FindPropertyRelative("description");
                    var actionConstraint  = action.FindPropertyRelative("axisConstraint");

                    EditorGUILayout.PropertyField(keyword, GUIContent.none, GUILayout.ExpandWidth(true));

                    Debug.Assert(allGestureLabels.Length == allGestureIds.Length);

                    var gestureLabels = new GUIContent[allGestureLabels.Length + 1];
                    var gestureIds    = new int[allGestureIds.Length + 1];

                    gestureLabels[0] = new GUIContent(((GestureInputType)gestureType.intValue).ToString());
                    gestureIds[0]    = gestureType.intValue;

                    for (int j = 0; j < allGestureLabels.Length; j++)
                    {
                        gestureLabels[j + 1] = allGestureLabels[j];
                        gestureIds[j + 1]    = allGestureIds[j];
                    }

                    EditorGUI.BeginChangeCheck();
                    gestureType.intValue = EditorGUILayout.IntPopup(GUIContent.none, gestureType.intValue, gestureLabels, gestureIds, GUILayout.Width(80f));

                    if (EditorGUI.EndChangeCheck())
                    {
                        serializedObject.ApplyModifiedProperties();
                        UpdateGestureLabels();
                    }

                    EditorGUI.BeginChangeCheck();

                    actionId.intValue = EditorGUILayout.IntPopup(GUIContent.none, actionId.intValue, actionLabels, actionIds, GUILayout.Width(64f));

                    if (EditorGUI.EndChangeCheck())
                    {
                        MixedRealityInputAction inputAction = actionId.intValue == 0 ? MixedRealityInputAction.None : MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions[actionId.intValue - 1];
                        actionDescription.stringValue   = inputAction.Description;
                        actionConstraint.enumValueIndex = (int)inputAction.AxisConstraint;
                        serializedObject.ApplyModifiedProperties();
                    }

                    if (GUILayout.Button(MinusButtonContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
                    {
                        list.DeleteArrayElementAtIndex(i);
                        serializedObject.ApplyModifiedProperties();
                        UpdateGestureLabels();
                    }

                    EditorGUILayout.EndHorizontal();
                }

                GUILayout.EndVertical();
                GUILayout.EndVertical();
            }
        }
        private void RenderList(SerializedProperty list)
        {
            // Disable speech commands if we could not initialize successfully
            using (new GUIEnabledWrapper(isInitialized, false))
            {
                EditorGUILayout.Space();
                GUILayout.BeginVertical();

                if (MixedRealityEditorUtility.RenderIndentedButton(AddButtonContent, EditorStyles.miniButton))
                {
                    list.arraySize += 1;
                    var speechCommand   = list.GetArrayElementAtIndex(list.arraySize - 1);
                    var localizationKey = speechCommand.FindPropertyRelative("localizationKey");
                    localizationKey.stringValue = string.Empty;
                    var keyword = speechCommand.FindPropertyRelative("keyword");
                    keyword.stringValue = string.Empty;
                    var keyCode = speechCommand.FindPropertyRelative("keyCode");
                    keyCode.intValue = (int)KeyCode.None;
                    var action   = speechCommand.FindPropertyRelative("action");
                    var actionId = action.FindPropertyRelative("id");
                    actionId.intValue = 0;
                }

                GUILayout.Space(12f);

                if (list == null || list.arraySize == 0)
                {
                    EditorGUILayout.HelpBox("Create a new Speech Command.", MessageType.Warning);
                    GUILayout.EndVertical();
                    return;
                }

                GUILayout.BeginVertical();

                GUILayout.BeginHorizontal();
                var labelWidth = EditorGUIUtility.labelWidth;
                EditorGUIUtility.labelWidth = 36f;
                EditorGUILayout.LabelField(LocalizationContent, GUILayout.ExpandWidth(true));
                EditorGUILayout.LabelField(KeywordContent, GUILayout.ExpandWidth(true));
                EditorGUILayout.LabelField(KeyCodeContent, GUILayout.Width(64f));
                EditorGUILayout.LabelField(ActionContent, GUILayout.Width(64f));
                EditorGUILayout.LabelField(string.Empty, GUILayout.Width(24f));
                EditorGUIUtility.labelWidth = labelWidth;
                GUILayout.EndHorizontal();

                for (int i = 0; i < list.arraySize; i++)
                {
                    EditorGUILayout.BeginHorizontal();
                    SerializedProperty speechCommand = list.GetArrayElementAtIndex(i);
                    var localizationKey = speechCommand.FindPropertyRelative("localizationKey");
                    EditorGUILayout.PropertyField(localizationKey, GUIContent.none, GUILayout.ExpandWidth(true));

                    var keyword = speechCommand.FindPropertyRelative("keyword");
                    EditorGUILayout.PropertyField(keyword, GUIContent.none, GUILayout.ExpandWidth(true));

                    var keyCode = speechCommand.FindPropertyRelative("keyCode");
                    EditorGUILayout.PropertyField(keyCode, GUIContent.none, GUILayout.Width(64f));

                    var action            = speechCommand.FindPropertyRelative("action");
                    var actionId          = action.FindPropertyRelative("id");
                    var actionDescription = action.FindPropertyRelative("description");
                    var actionConstraint  = action.FindPropertyRelative("axisConstraint");

                    EditorGUI.BeginChangeCheck();
                    actionId.intValue = EditorGUILayout.IntPopup(GUIContent.none, actionId.intValue, actionLabels, actionIds, GUILayout.Width(64f));

                    if (EditorGUI.EndChangeCheck())
                    {
                        MixedRealityInputAction inputAction = actionId.intValue == 0 ? MixedRealityInputAction.None : MixedRealityToolkit.Instance.ActiveProfile.InputSystemProfile.InputActionsProfile.InputActions[actionId.intValue - 1];
                        actionDescription.stringValue   = inputAction.Description;
                        actionConstraint.enumValueIndex = (int)inputAction.AxisConstraint;
                    }

                    if (GUILayout.Button(MinusButtonContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
                    {
                        list.DeleteArrayElementAtIndex(i);
                    }

                    EditorGUILayout.EndHorizontal();
                }

                GUILayout.EndVertical();
                GUILayout.EndVertical();
            }
        }