public void TestConvertToValidFilename()
        {
            // test already valid filenames
            var filename = "foobar.fbx";
            var result   = ModelExporter.ConvertToValidFilename(filename);

            Assert.AreEqual(filename, result);

            filename = "foo_bar 1.fbx";
            result   = ModelExporter.ConvertToValidFilename(filename);
            Assert.AreEqual(filename, result);

            // test invalid filenames
            filename = "?foo**bar///.fbx";
            result   = ModelExporter.ConvertToValidFilename(filename);
#if UNITY_EDITOR_WIN
            Assert.AreEqual("_foo__bar___.fbx", result);
#else
            Assert.AreEqual("?foo**bar___.fbx", result);
#endif

            filename = "foo$?ba%r 2.fbx";
            result   = ModelExporter.ConvertToValidFilename(filename);
#if UNITY_EDITOR_WIN
            Assert.AreEqual("foo$_ba%r 2.fbx", result);
#else
            Assert.AreEqual("foo$?ba%r 2.fbx", result);
#endif
        }
        /// <summary>
        /// Create a prefab from 'instance', or apply 'instance' to its
        /// prefab if it's already an instance of a prefab.
        ///
        /// If it's an instance of a model prefab (an fbx file) we will
        /// lose that connection and point to a new prefab file.
        ///
        /// To avoid applying to an existing prefab, break the prefab
        /// connection with <c>PrefabUtility.DisconnectPrefabInstance</c>.
        ///
        /// Returns the new or updated prefab.
        /// </summary>
        /// <param name="instance">A GameObject in the scene. After this
        /// call, it will be a prefab instance (it might already be
        /// one).</param>
        /// <param name="prefabFullPath">The full path to a prefab file we
        /// will create. Ignored if <paramref name="instance"/> is a
        /// prefab instance already. May be null, in which case we'll use <paramref
        /// name="prefabDirectoryFullPath"/> to generate a new name</param>
        /// <param name="prefabDirectoryFullPath">The full path to a
        /// directory that will hold the new prefab. Ignored if <paramref
        /// name="instance"/> is a prefab instance already. Ignored if
        /// <paramref name="prefabFullPath"/> is provided. May be null, in
        /// which case we'll use the project export settings.</param>
        /// <returns>The new or existing prefab.</returns>
        public static GameObject ApplyOrCreatePrefab(GameObject instance,
                                                     string prefabDirectoryFullPath = null,
                                                     string prefabFullPath          = null)
        {
            if (instance == null)
            {
                throw new System.ArgumentNullException("instance");
            }

            if (PrefabUtility.GetPrefabType(instance) == PrefabType.PrefabInstance)
            {
                return(PrefabUtility.ReplacePrefab(instance, PrefabUtility.GetCorrespondingObjectFromSource(instance)));
            }

            // Otherwise, create a new prefab. First choose its filename/path.
            if (string.IsNullOrEmpty(prefabFullPath))
            {
                // Generate a unique filename.
                if (string.IsNullOrEmpty(prefabDirectoryFullPath))
                {
                    prefabDirectoryFullPath = UnityEditor.Formats.Fbx.Exporter.ExportSettings.PrefabAbsoluteSavePath;
                }
                else
                {
                    prefabDirectoryFullPath = Path.GetFullPath(prefabDirectoryFullPath);
                }
                var prefabBasename = ModelExporter.ConvertToValidFilename(instance.name + ".prefab");

                prefabFullPath = Path.Combine(prefabDirectoryFullPath, prefabBasename);
                if (File.Exists(prefabFullPath))
                {
                    prefabFullPath = IncrementFileName(prefabDirectoryFullPath, prefabFullPath);
                }
            }
            var prefabProjectRelativePath = ExportSettings.GetProjectRelativePath(prefabFullPath);
            var prefabFileName            = Path.ChangeExtension(prefabProjectRelativePath, ".prefab");

            var prefab = PrefabUtility.CreatePrefab(prefabFileName, instance, ReplacePrefabOptions.ConnectToPrefab);

            if (!prefab)
            {
                throw new ConvertToLinkedPrefabException(string.Format("Failed to create prefab asset in [{0}]", prefabFileName));
            }
            return(prefab);
        }
        // -------------------------------------------------------------------------------------

        protected void OnGUI()
        {
            // Increasing the label width so that none of the text gets cut off
            EditorGUIUtility.labelWidth = LabelWidth;

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();

            #if UNITY_2018_1_OR_NEWER
            if (EditorGUILayout.DropdownButton(presetIcon, FocusType.Keyboard, presetIconButton))
            {
                ShowPresetReceiver();
            }
            #endif

            GUILayout.EndHorizontal();

            EditorGUILayout.LabelField("Naming");
            EditorGUI.indentLevel++;

            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Export Name",
                                           "Filename to save model to."), GUILayout.Width(LabelWidth - TextFieldAlignOffset));

            EditorGUI.BeginDisabledGroup(DisableNameSelection);
            // Show the export name with an uneditable ".fbx" at the end
            //-------------------------------------
            EditorGUILayout.BeginVertical();
            EditorGUILayout.BeginHorizontal(EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            EditorGUI.indentLevel--;
            // continually resize to contents
            var textFieldSize = NameTextFieldStyle.CalcSize(new GUIContent(ExportFileName));
            ExportFileName = EditorGUILayout.TextField(ExportFileName, NameTextFieldStyle, GUILayout.Width(textFieldSize.x + 5), GUILayout.MinWidth(5));
            ExportFileName = ModelExporter.ConvertToValidFilename(ExportFileName);

            EditorGUILayout.LabelField("<color=#808080ff>.fbx</color>", FbxExtLabelStyle, GUILayout.Width(FbxExtLabelWidth));
            EditorGUI.indentLevel++;

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();
            //-----------------------------------
            EditorGUI.EndDisabledGroup();
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Export Path",
                                           "Location where the FBX will be saved."), GUILayout.Width(LabelWidth - FieldOffset));

            var pathLabels = ExportSettings.GetMixedSavePaths(FbxSavePaths);

            if (this is ConvertToPrefabEditorWindow)
            {
                pathLabels = ExportSettings.GetRelativeFbxSavePaths(FbxSavePaths, ref m_selectedFbxPath);
            }

            SelectedFbxPath = EditorGUILayout.Popup(SelectedFbxPath, pathLabels, GUILayout.MinWidth(SelectableLabelMinWidth));

            if (!(this is ConvertToPrefabEditorWindow))
            {
                var exportSettingsEditor = InnerEditor as ExportModelSettingsEditor;
                // Set export setting for exporting outside the project on choosing a path
                var exportOutsideProject = !pathLabels[SelectedFbxPath].Substring(0, 6).Equals("Assets");
                exportSettingsEditor.SetExportingOutsideProject(exportOutsideProject);
            }

            if (GUILayout.Button(new GUIContent("...", "Browse to a new location to export to"), EditorStyles.miniButton, GUILayout.Width(BrowseButtonWidth)))
            {
                string initialPath = Application.dataPath;

                string fullPath = EditorUtility.OpenFolderPanel(
                    "Select Export Model Path", initialPath, null
                    );

                // Unless the user canceled, save path.
                if (!string.IsNullOrEmpty(fullPath))
                {
                    var relativePath = ExportSettings.ConvertToAssetRelativePath(fullPath);

                    // If exporting an fbx for a prefab, not allowed to export outside the Assets folder
                    if (this is ConvertToPrefabEditorWindow && string.IsNullOrEmpty(relativePath))
                    {
                        Debug.LogWarning("Please select a location in the Assets folder");
                    }
                    // We're exporting outside Assets folder, so store the absolute path
                    else if (string.IsNullOrEmpty(relativePath))
                    {
                        ExportSettings.AddSavePath(fullPath, FbxSavePaths, exportOutsideProject: true);
                        SelectedFbxPath = 0;
                    }
                    // Store the relative path to the Assets folder
                    else
                    {
                        ExportSettings.AddSavePath(relativePath, FbxSavePaths, exportOutsideProject: false);
                        SelectedFbxPath = 0;
                    }
                    // Make sure focus is removed from the selectable label
                    // otherwise it won't update
                    GUIUtility.hotControl      = 0;
                    GUIUtility.keyboardControl = 0;
                }
            }
            GUILayout.EndHorizontal();

            CreateCustomUI();

            EditorGUILayout.Space();

            EditorGUI.BeginDisabledGroup(DisableTransferAnim);
            EditorGUI.indentLevel--;
            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Transfer Animation",
                                           "Transfer transform animation from source to destination. Animation on objects between source and destination will also be transferred to destination."
                                           ), GUILayout.Width(LabelWidth - FieldOffset));
            GUILayout.EndHorizontal();
            EditorGUI.indentLevel++;
            TransferAnimationSource = EditorGUILayout.ObjectField("Source", TransferAnimationSource, typeof(Transform), allowSceneObjects: true) as Transform;
            TransferAnimationDest   = EditorGUILayout.ObjectField("Destination", TransferAnimationDest, typeof(Transform), allowSceneObjects: true) as Transform;
            EditorGUILayout.Space();
            EditorGUI.EndDisabledGroup();

            EditorGUI.indentLevel--;
            m_showOptions = EditorGUILayout.Foldout(m_showOptions, "Options");
            EditorGUI.indentLevel++;
            if (m_showOptions)
            {
                InnerEditor.OnInspectorGUI();
            }

            // if we are exporting or converting a prefab with overrides, then show a warning
            if (SelectionContainsPrefabInstanceWithAddedObjects())
            {
                EditorGUILayout.Space();
                EditorGUILayout.HelpBox("Prefab instance overrides will be exported", MessageType.Warning, true);
            }

            GUILayout.FlexibleSpace();

            GUILayout.BeginHorizontal();
            DoNotShowDialogUI();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("Cancel", GUILayout.Width(ExportButtonWidth)))
            {
                this.Close();
            }

            if (GUILayout.Button(ExportButtonName, GUILayout.Width(ExportButtonWidth)))
            {
                if (Export())
                {
                    this.Close();
                }
            }
            GUILayout.EndHorizontal();
            EditorGUILayout.Space(); // adding a space at bottom of dialog so buttons aren't right at the edge

            if (GUI.changed)
            {
                SaveExportSettings();
            }
        }
        /// <summary>
        /// Return an FBX asset that corresponds to 'toConvert'.
        ///
        /// If 'toConvert' is the root of an FBX asset, return it.
        ///
        /// If it's an instance in a scene the points to the root of an FBX
        /// asset, return that asset.
        ///
        /// Otherwise, export according to the paths and options, and
        /// return the new asset.
        /// </summary>
        /// <param name="toConvert">GameObject for which we want an fbx asset</param>
        /// <param name="fbxDirectoryFullPath">Export will choose an
        /// appropriate filename in this directory. Ignored if fbxFullPath is
        /// set. Ignored if toConvert is an fbx asset or an instance of an
        /// fbx.</param>
        /// <param name="fbxDirectoryFullPath">Export will create this
        /// file. Overrides fbxDirectoryFullPath. Ignored if toConvert is an
        /// fbx asset or an instance of an fbx.</param>
        /// <returns>The root of a model prefab asset.</returns>
        internal static GameObject GetOrCreateFbxAsset(GameObject toConvert,
                                                       string fbxDirectoryFullPath = null,
                                                       string fbxFullPath          = null,
                                                       ConvertToPrefabSettingsSerialize exportOptions = null)
        {
            if (toConvert == null)
            {
                throw new System.ArgumentNullException("toConvert");
            }

            var mainAsset = GetFbxAssetOrNull(toConvert);

            if (mainAsset)
            {
                return(mainAsset);
            }

            if (string.IsNullOrEmpty(fbxFullPath))
            {
                // Generate a unique filename.
                if (string.IsNullOrEmpty(fbxDirectoryFullPath))
                {
                    fbxDirectoryFullPath = UnityEditor.Formats.Fbx.Exporter.ExportSettings.FbxAbsoluteSavePath;
                }
                else
                {
                    fbxDirectoryFullPath = Path.GetFullPath(fbxDirectoryFullPath);
                }
                var fbxBasename = ModelExporter.ConvertToValidFilename(toConvert.name + ".fbx");

                fbxFullPath = Path.Combine(fbxDirectoryFullPath, fbxBasename);
                if (File.Exists(fbxFullPath))
                {
                    fbxFullPath = IncrementFileName(fbxDirectoryFullPath, fbxFullPath);
                }
            }
            var projectRelativePath = ExportSettings.GetProjectRelativePath(fbxFullPath);

            // Make sure that the object names in the hierarchy are unique.
            // The import back in to Unity would do this automatically but
            // we prefer to control it so that the Maya artist can see the
            // same names as exist in Unity.
            EnforceUniqueNames(new GameObject[] { toConvert });

            // Export to FBX. It refreshes the database.
            {
                var fbxActualPath = ModelExporter.ExportObject(
                    fbxFullPath, toConvert,
                    exportOptions != null ? exportOptions : new ConvertToPrefabSettingsSerialize()
                    );
                if (fbxActualPath != fbxFullPath)
                {
                    throw new ConvertToNestedPrefabException("Failed to convert " + toConvert.name);
                }
            }

            // Replace w Model asset. LoadMainAssetAtPath wants a path
            // relative to the project, not relative to the assets folder.
            var unityMainAsset = AssetDatabase.LoadMainAssetAtPath(projectRelativePath) as GameObject;

            if (!unityMainAsset)
            {
                throw new ConvertToNestedPrefabException("Failed to convert " + toConvert.name);
            }

            return(unityMainAsset);
        }
        public static GameObject Convert(
            GameObject toConvert,
            string fbxDirectoryFullPath    = null,
            string fbxFullPath             = null,
            string prefabDirectoryFullPath = null,
            string prefabFullPath          = null,
            ConvertToPrefabSettingsSerialize exportOptions = null)
        {
            if (toConvert == null)
            {
                throw new System.ArgumentNullException("toConvert");
            }

            if (PrefabUtility.IsPartOfNonAssetPrefabInstance(toConvert) && !PrefabUtility.IsOutermostPrefabInstanceRoot(toConvert))
            {
                return(null); // cannot convert in this scenario
            }

            // can't currently handle converting root of prefab in prefab preview scene
            if (SceneManagement.EditorSceneManager.IsPreviewSceneObject(toConvert) && toConvert.transform.parent == null)
            {
                return(null);
            }

            // If we selected the something that's already backed by an
            // FBX, don't export.
            var mainAsset = GetOrCreateFbxAsset(toConvert, fbxDirectoryFullPath, fbxFullPath, exportOptions);

            // if toConvert is part of a prefab asset and not an instance, make it an instance in a preview scene
            // so that we can unpack it and avoid issues with nested prefab references.
            bool isPrefabAsset = false;

            UnityEngine.SceneManagement.Scene?previewScene = null;
            if (PrefabUtility.IsPartOfPrefabAsset(toConvert) && PrefabUtility.GetPrefabInstanceStatus(toConvert) == PrefabInstanceStatus.NotAPrefab)
            {
                previewScene  = SceneManagement.EditorSceneManager.NewPreviewScene();
                toConvert     = PrefabUtility.InstantiatePrefab(toConvert, previewScene.Value) as GameObject;
                isPrefabAsset = true;
            }

            // don't need to undo if we are converting a prefab asset
            if (!isPrefabAsset)
            {
                Undo.IncrementCurrentGroup();
                Undo.SetCurrentGroupName(string.Format(UndoConversionGroup, toConvert.name));
            }

            // if root is a prefab instance, unpack it. Unpack everything below as well
            if (PrefabUtility.GetPrefabInstanceStatus(toConvert) == PrefabInstanceStatus.Connected)
            {
                Undo.RegisterFullObjectHierarchyUndo(toConvert, "unpack prefab instance");
                PrefabUtility.UnpackPrefabInstance(toConvert, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
            }

            // create prefab variant from the fbx
            var fbxInstance = PrefabUtility.InstantiatePrefab(mainAsset) as GameObject;

            // replace hierarchy in the scene
            if (!isPrefabAsset && toConvert != null)
            {
                // don't worry about keeping the world position in the prefab, as we will fix the transform on the instance root
                fbxInstance.transform.SetParent(toConvert.transform.parent, worldPositionStays: false);
                fbxInstance.transform.SetSiblingIndex(toConvert.transform.GetSiblingIndex());
            }

            // copy components over
            UpdateFromSourceRecursive(fbxInstance, toConvert);

            // make sure we have a path for the prefab
            if (string.IsNullOrEmpty(prefabFullPath))
            {
                // Generate a unique filename.
                if (string.IsNullOrEmpty(prefabDirectoryFullPath))
                {
                    prefabDirectoryFullPath = UnityEditor.Formats.Fbx.Exporter.ExportSettings.PrefabAbsoluteSavePath;
                }
                else
                {
                    prefabDirectoryFullPath = Path.GetFullPath(prefabDirectoryFullPath);
                }
                var prefabBasename = ModelExporter.ConvertToValidFilename(toConvert.name + ".prefab");

                prefabFullPath = Path.Combine(prefabDirectoryFullPath, prefabBasename);
                if (File.Exists(prefabFullPath))
                {
                    prefabFullPath = IncrementFileName(prefabDirectoryFullPath, prefabFullPath);
                }
            }
            // make sure the directory structure exists
            var dirName = Path.GetDirectoryName(prefabFullPath);

            if (!Directory.Exists(dirName))
            {
                Directory.CreateDirectory(dirName);
            }
            var prefab = PrefabUtility.SaveAsPrefabAssetAndConnect(fbxInstance, ExportSettings.GetProjectRelativePath(prefabFullPath), InteractionMode.AutomatedAction);

            // replace hierarchy in the scene
            if (!isPrefabAsset && toConvert != null)
            {
                Undo.DestroyObjectImmediate(toConvert);
                Undo.RegisterCreatedObjectUndo(fbxInstance, UndoConversionCreateObject);
                SceneManagement.EditorSceneManager.MarkSceneDirty(fbxInstance.scene);

                Undo.IncrementCurrentGroup();
                return(fbxInstance);
            }
            else
            {
                Undo.ClearUndo(toConvert);
                Undo.ClearUndo(fbxInstance);
                Object.DestroyImmediate(fbxInstance);
                Object.DestroyImmediate(toConvert);
            }
            if (previewScene.HasValue)
            {
                SceneManagement.EditorSceneManager.ClosePreviewScene(previewScene.Value);
            }

            return(prefab);
        }
Exemple #6
0
        protected override void CreateCustomUI()
        {
            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Prefab Name",
                                           "Filename to save prefab to."), GUILayout.Width(LabelWidth - TextFieldAlignOffset));

            EditorGUI.BeginDisabledGroup(DisableNameSelection);
            // Show the export name with an uneditable ".prefab" at the end
            //-------------------------------------
            EditorGUILayout.BeginVertical();
            EditorGUILayout.BeginHorizontal(EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            EditorGUI.indentLevel--;
            // continually resize to contents
            var textFieldSize = NameTextFieldStyle.CalcSize(new GUIContent(m_prefabFileName));

            m_prefabFileName = EditorGUILayout.TextField(m_prefabFileName, NameTextFieldStyle, GUILayout.Width(textFieldSize.x + 5), GUILayout.MinWidth(5));
            m_prefabFileName = ModelExporter.ConvertToValidFilename(m_prefabFileName);

            EditorGUILayout.LabelField("<color=#808080ff>.prefab</color>", FbxExtLabelStyle, GUILayout.Width(m_prefabExtLabelWidth));
            EditorGUI.indentLevel++;

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();
            //-----------------------------------
            EditorGUI.EndDisabledGroup();
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Prefab Path",
                                           "Relative path for saving FBX Prefab Variants."), GUILayout.Width(LabelWidth - FieldOffset));

            var pathLabels = ExportSettings.GetRelativePrefabSavePaths();

            ExportSettings.instance.SelectedPrefabPath = EditorGUILayout.Popup(ExportSettings.instance.SelectedPrefabPath, pathLabels, GUILayout.MinWidth(SelectableLabelMinWidth));

            if (GUILayout.Button(new GUIContent("...", "Browse to a new location to save prefab to"), EditorStyles.miniButton, GUILayout.Width(BrowseButtonWidth)))
            {
                string initialPath = Application.dataPath;

                string fullPath = EditorUtility.OpenFolderPanel(
                    "Select FBX Prefab Variant Save Path", initialPath, null
                    );

                // Unless the user canceled, make sure they chose something in the Assets folder.
                if (!string.IsNullOrEmpty(fullPath))
                {
                    var relativePath = ExportSettings.ConvertToAssetRelativePath(fullPath);
                    if (string.IsNullOrEmpty(relativePath))
                    {
                        Debug.LogWarning("Please select a location in the Assets folder");
                    }
                    else
                    {
                        ExportSettings.AddPrefabSavePath(relativePath);

                        // Make sure focus is removed from the selectable label
                        // otherwise it won't update
                        GUIUtility.hotControl      = 0;
                        GUIUtility.keyboardControl = 0;
                    }
                }
            }
            GUILayout.EndHorizontal();
        }
        // -------------------------------------------------------------------------------------

        protected void OnGUI()
        {
            // Increasing the label width so that none of the text gets cut off
            EditorGUIUtility.labelWidth = LabelWidth;

            GUILayout.BeginHorizontal();
            GUILayout.FlexibleSpace();

            #if UNITY_2018_1_OR_NEWER
            if (EditorGUILayout.DropdownButton(presetIcon, FocusType.Keyboard, presetIconButton))
            {
                ShowPresetReceiver();
            }
            #endif

            GUILayout.EndHorizontal();

            EditorGUILayout.LabelField("Naming");
            EditorGUI.indentLevel++;

            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Export Name",
                                           "Filename to save model to."), GUILayout.Width(LabelWidth - TextFieldAlignOffset));

            EditorGUI.BeginDisabledGroup(DisableNameSelection);
            // Show the export name with an uneditable ".fbx" at the end
            //-------------------------------------
            EditorGUILayout.BeginVertical();
            EditorGUILayout.BeginHorizontal(EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
            EditorGUI.indentLevel--;
            // continually resize to contents
            var textFieldSize = NameTextFieldStyle.CalcSize(new GUIContent(ExportFileName));
            ExportFileName = EditorGUILayout.TextField(ExportFileName, NameTextFieldStyle, GUILayout.Width(textFieldSize.x + 5), GUILayout.MinWidth(5));
            ExportFileName = ModelExporter.ConvertToValidFilename(ExportFileName);

            EditorGUILayout.LabelField("<color=#808080ff>.fbx</color>", FbxExtLabelStyle, GUILayout.Width(FbxExtLabelWidth));
            EditorGUI.indentLevel++;

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();
            //-----------------------------------
            EditorGUI.EndDisabledGroup();
            GUILayout.EndHorizontal();

            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Export Path",
                                           "Relative path for saving Model Prefabs."), GUILayout.Width(LabelWidth - FieldOffset));

            var pathLabels = ExportSettings.GetRelativeFbxSavePaths();

            ExportSettings.instance.SelectedFbxPath = EditorGUILayout.Popup(ExportSettings.instance.SelectedFbxPath, pathLabels, GUILayout.MinWidth(SelectableLabelMinWidth));

            if (GUILayout.Button(new GUIContent("...", "Browse to a new location to export to"), EditorStyles.miniButton, GUILayout.Width(BrowseButtonWidth)))
            {
                string initialPath = Application.dataPath;

                string fullPath = EditorUtility.OpenFolderPanel(
                    "Select Export Model Path", initialPath, null
                    );

                // Unless the user canceled, make sure they chose something in the Assets folder.
                if (!string.IsNullOrEmpty(fullPath))
                {
                    var relativePath = ExportSettings.ConvertToAssetRelativePath(fullPath);
                    if (string.IsNullOrEmpty(relativePath))
                    {
                        Debug.LogWarning("Please select a location in the Assets folder");
                    }
                    else
                    {
                        ExportSettings.AddFbxSavePath(relativePath);

                        // Make sure focus is removed from the selectable label
                        // otherwise it won't update
                        GUIUtility.hotControl      = 0;
                        GUIUtility.keyboardControl = 0;
                    }
                }
            }
            GUILayout.EndHorizontal();

            CreateCustomUI();

            EditorGUILayout.Space();

            EditorGUI.BeginDisabledGroup(DisableTransferAnim);
            EditorGUI.indentLevel--;
            GUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(new GUIContent(
                                           "Transfer Animation",
                                           "Transfer transform animation from source to destination. Animation on objects between source and destination will also be transferred to destination."
                                           ), GUILayout.Width(LabelWidth - FieldOffset));
            GUILayout.EndHorizontal();
            EditorGUI.indentLevel++;
            TransferAnimationSource = EditorGUILayout.ObjectField("Source", TransferAnimationSource, typeof(Transform), allowSceneObjects: true) as Transform;
            TransferAnimationDest   = EditorGUILayout.ObjectField("Destination", TransferAnimationDest, typeof(Transform), allowSceneObjects: true) as Transform;
            EditorGUILayout.Space();
            EditorGUI.EndDisabledGroup();

            EditorGUI.indentLevel--;
            m_showOptions = EditorGUILayout.Foldout(m_showOptions, "Options");
            EditorGUI.indentLevel++;
            if (m_showOptions)
            {
                InnerEditor.OnInspectorGUI();
            }

            GUILayout.FlexibleSpace();

            GUILayout.BeginHorizontal();
            DoNotShowDialogUI();
            GUILayout.FlexibleSpace();
            if (GUILayout.Button("Cancel", GUILayout.Width(ExportButtonWidth)))
            {
                this.Close();
            }

            if (GUILayout.Button(ExportButtonName, GUILayout.Width(ExportButtonWidth)))
            {
                if (Export())
                {
                    this.Close();
                }
            }
            GUILayout.EndHorizontal();

            if (GUI.changed)
            {
                SaveExportSettings();
            }
        }