/// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="importBaseDir">
        /// The base project directory for saving the imported
        /// Unity assets (e.g. "Assets/Imported/MyModel").
        /// </param>
        public EditorGltfImportCache(string importBaseDir)
        {
            Textures  = new SerializedAssetList <Texture2D>(SerializeTexture);
            Materials = new SerializedAssetList <Material>(SerializeMaterial);
            Meshes    = new SerializedAssetList <List <KeyValuePair <Mesh, Material> > >
                            (SerializeMesh);

            // create directory structure for imported assets

            _importBaseDir = importBaseDir;
            Directory.CreateDirectory(
                UnityPathUtil.GetAbsolutePath(_importBaseDir));

            _importTexturesDir = Path.Combine(_importBaseDir, "Textures");
            Directory.CreateDirectory(
                UnityPathUtil.GetAbsolutePath(_importTexturesDir));

            _importMaterialsDir = Path.Combine(_importBaseDir, "Materials");
            Directory.CreateDirectory(
                UnityPathUtil.GetAbsolutePath(_importMaterialsDir));

            _importMeshesDir = Path.Combine(_importBaseDir, "Meshes");
            Directory.CreateDirectory(
                UnityPathUtil.GetAbsolutePath(_importMeshesDir));
        }
Пример #2
0
        /// <summary>
        /// Create a prefab from the imported hierarchy of game objects.
        /// This is the final output of an Editor glTF import.
        /// </summary>
        protected IEnumerator <GameObject> CreatePrefabEnum()
        {
            string basename = "scene.prefab";

            if (!String.IsNullOrEmpty(_imported.Scene.name))
            {
                basename = String.Format("{0}.prefab",
                                         GLTFUtils.cleanName(_imported.Scene.name));
            }

            string dir  = UnityPathUtil.GetProjectPath(_importPath);
            string path = Path.Combine(dir, basename);

            GameObject prefab =
                PrefabUtility.SaveAsPrefabAsset(_imported.Scene, path);

            // Make the prefab visible.
            //
            // Note: The model hierarchy is kept hidden during the glTF import
            // so that the user never sees the partially reconstructed
            // model.

            prefab.SetActive(true);

            // Note: base.Clear() removes imported game objects from
            // the scene and from memory, but does not remove imported
            // asset files from disk.

            base.Clear();

            yield return(prefab);
        }
Пример #3
0
        /// <summary>
        /// Save the input mesh to disk as a set of Unity .asset
        /// files and return a new mesh. The input mesh is a list
        /// mesh primitives, where each primitive is a KeyValuePair
        /// of a Mesh and a Material.  The returned mesh (i.e. list of primitives)
        /// is the same as the input list, except that the Mesh
        /// for each primitive has been replaced by one that is backed
        /// by a Unity .asset file.  These Mesh objects know about
        /// their backing .asset file and will automatically sync
        /// in-memory changes to the Mesh to disk. (For further
        /// info, see the Unity documentation for AssetDatabase.)
        /// </summary>
        /// <param name="mesh">
        /// The mesh (list of mesh primitives) to be serialized to disk.
        /// </param>
        /// <returns>
        /// A new mesh (list of mesh primitives) that is backed by a
        /// set of .asset files (one per mesh primitive).
        /// </returns>
        protected List <KeyValuePair <Mesh, Material> > SerializeMesh(
            int index, List <KeyValuePair <Mesh, Material> > mesh)
        {
            Directory.CreateDirectory(UnityPathUtil.GetAbsolutePath(_importMeshesDir));

            for (int i = 0; i < mesh.Count; ++i)
            {
                Mesh     primitiveMesh     = mesh[i].Key;
                Material primitiveMaterial = mesh[i].Value;

                string basename = String.Format("{0}.asset", primitiveMesh.name);
                string path     = Path.Combine(_importMeshesDir, basename);

                // Serialize the mesh to disk as a Unity asset.
                //
                // Note: The primitiveMaterial does not need
                // to be serialized here, since that has already
                // been done during the earlier material-importing
                // step.

                AssetDatabase.CreateAsset(primitiveMesh, path);
                AssetDatabase.Refresh();
                primitiveMesh = (Mesh)AssetDatabase.LoadAssetAtPath(
                    path, typeof(Mesh));

                mesh[i] = new KeyValuePair <Mesh, Material>(
                    primitiveMesh, primitiveMaterial);
            }

            return(mesh);
        }
Пример #4
0
        /// <summary>
        /// Remove any imported game objects from scene and from memory,
        /// and remove any asset files that were generated.
        /// </summary>
        protected override void Clear()
        {
            // remove imported game objects from scene and from memory
            base.Clear();

            // remove Unity asset files that were created during import
            UnityPathUtil.RemoveProjectDir(_importPath);
        }
Пример #5
0
 /// <summary>
 /// Constructor
 /// </summary>
 public EditorGltfImporter(string gltfPath, string importPath,
                           ProgressCallback progressCallback = null)
     : base(new Uri(gltfPath), null,
            new EditorGltfImportCache(UnityPathUtil.GetProjectPath(importPath)),
            progressCallback)
 {
     _importPath = importPath;
 }
Пример #6
0
        /// <summary>
        /// Save the given AnimationClip to disk as a Unity asset
        /// and return a new AnimationClip. The returned AnimationClip
        /// is the same as the original, except that it knows
        /// about the .anim file that backs it and will automatically
        /// synchronize in-memory changes to disk. (For further
        /// info, see the Unity documentation for AssetDatabase.)
        /// </summary>
        /// <param name="clip">
        /// The AnimationClip to be serialized to disk
        /// </param>
        /// <returns>
        /// A new AnimationClip that is backed by a .anim file
        /// </returns>
        private AnimationClip SerializeAnimationClip(int index, AnimationClip clip)
        {
            Directory.CreateDirectory(UnityPathUtil.GetAbsolutePath(_importAnimationsDir));

            string basename = string.Format("{0}.anim", clip.name);
            string path     = Path.Combine(_importAnimationsDir, basename);

            AssetDatabase.CreateAsset(clip, path);
            AssetDatabase.Refresh();
            clip = (AnimationClip)AssetDatabase.LoadAssetAtPath(
                path, typeof(AnimationClip));

            return(clip);
        }
Пример #7
0
        /// <summary>
        /// Save the given material to disk as a Unity asset
        /// and return a new Material. The returned Material
        /// is the same as the original, except that it knows
        /// about the .mat file that backs it and will automatically
        /// synchronize in-memory changes to disk. (For further
        /// info, see the Unity documentation for AssetDatabase.)
        /// </summary>
        /// <param name="material">
        /// The material to be serialized to disk
        /// </param>
        /// <returns>
        /// A new Material that is backed by a .mat file
        /// </returns>
        protected Material SerializeMaterial(int index, Material material)
        {
            Directory.CreateDirectory(UnityPathUtil.GetAbsolutePath(_importMaterialsDir));

            string basename = String.Format("{0}.mat", material.name);
            string path     = Path.Combine(_importMaterialsDir, basename);

            AssetDatabase.CreateAsset(material, path);
            AssetDatabase.Refresh();
            material = (Material)AssetDatabase.LoadAssetAtPath(
                path, typeof(Material));

            return(material);
        }
        /// <summary>
        /// Save the given texture to disk as a Unity asset and
        /// return a new Texture2D. The returned Texture2D
        /// is the same as the original, except that it
        /// knows about the asset file that backs it and will
        /// automatically synchronize in-memory changes to disk.
        /// (For further info, see the Unity documentation for
        /// AssetDatabase.)
        /// </summary>
        /// <param name="texture">
        /// The texture to be serialized to disk.
        /// </param>
        /// <returns>
        /// A new Texture2D that is backed by an asset file.
        /// </returns>
        protected Texture2D SerializeTexture(int index, Texture2D texture)
        {
            // Unity's Texture2D.LoadImage() method imports
            // .png/.jpg images upside down, so flip it
            // right side up again.
            texture = TextureUtil.FlipTexture(texture);

            string basename = String.Format("texture_{0}.png", index);
            string pngPath  = Path.Combine(_importTexturesDir, basename);

            byte[] pngData = texture.EncodeToPNG();
            File.WriteAllBytes(UnityPathUtil.GetAbsolutePath(pngPath), pngData);

            AssetDatabase.Refresh();
            texture = (Texture2D)AssetDatabase.LoadAssetAtPath(
                pngPath, typeof(Texture2D));

            return(texture);
        }
Пример #9
0
        /// <summary>
        /// Create a prefab from the imported hierarchy of game objects.
        /// This is the final output of an Editor glTF import.
        /// </summary>
        protected IEnumerator <GameObject> CreatePrefabEnum()
        {
            string basename = "scene.prefab";

            if (!String.IsNullOrEmpty(_imported.Scene.name))
            {
                basename = String.Format("{0}.prefab",
                                         GLTFUtils.cleanName(_imported.Scene.name));
            }

            string dir  = UnityPathUtil.GetProjectPath(_importPath);
            string path = Path.Combine(dir, basename);

            GameObject prefab =
                PrefabUtility.SaveAsPrefabAsset(_imported.Scene, path);

            // Note: base.Clear() removes imported game objects from
            // the scene and from memory, but does not remove imported
            // asset files from disk.
            base.Clear();

            yield return(prefab);
        }
        /// <summary>
        /// Callback that is invoked for each item (file or folder) that
        /// is currently visible in the left/right panes of the Project Browser.
        /// </summary>
        /// <param name="guid"></param>
        /// <param name="selectionRect"></param>
        private static void ProjectItemOnGUI(string guid, Rect selectionRect)
        {
            Event @event = Event.current;

            // Get a reference to the Project Browser window that the user last
            // interacted with. (It is possible to have multiple Project Browsers
            // open at the same time.)
            //
            // Note: There is no Unity API for doing this, so we have to use
            // reflection to access private/internal members.

            var projectBrowser = ProjectBrowserExtensions
                                 .GetLastInteractedProjectBrowser();

            // Get the position of the Project Browser window, in screen coordinates.

            Vector2 projectBrowserPosition = ((EditorWindow)projectBrowser).position.position;

            // For each event, we build a mapping of items -> rects. This mapping
            // is needed to determine if our drop target is located inside a tree
            // area or a list area, and to determine if the drop target is outside
            // of any items (e.g. dropping into an empty area of the files pane).
            //
            // I use `@event.rawType` here instead of `@event.type` because
            // `@event.type` is set to `EventType.Ignore` when the mouse is located
            // outside of the tree/list area containing the current item
            // (identified by `guid`).

            if (@event.rawType != _currentEventType)
            {
                _currentEventType = @event.rawType;

                List <ItemRect> temp = _itemRectsLastFrame;
                _itemRectsLastFrame = _itemRectsThisFrame;
                _itemRectsThisFrame = temp;
                _itemRectsThisFrame.Clear();
            }

            Vector2 localMousePosition  = Event.current.mousePosition;
            Vector2 globalMousePosition = GUIUtility.GUIToScreenPoint(localMousePosition);

            // Record guid => screen rect mapping for the current item.

            Rect globalRect = new Rect(
                GUIUtility.GUIToScreenPoint(selectionRect.position),
                selectionRect.size);

            // Note: Non-assets in the Project Browser (e.g. "Favorites", "Packages")
            // have empty GUIDs.

            if (guid.Length > 0)
            {
                _itemRectsThisFrame.Add(new ItemRect {
                    guid = guid, rect = globalRect
                });
            }

            // Note: When file(s) are dragged onto the right pane, the left pane
            // will have `@event.type == EventType.Ignore` and `@event.rawType ==
            // EventType.DragPerform`. (And likewise for items in the right pane
            // when file(s) are dragged onto the left pane.) I'm not 100% sure why
            // this happens, but I think it because the mouse events are outside
            // the active GUI clip area (e.g. `GUI.BeginClip()`,
            // `GUI.BeginScrollRect()`).

            if (@event.type == EventType.DragPerform || @event.rawType == EventType.DragPerform)
            {
                // Determine the target directory for the GLTF import (i.e.
                // directory where the model prefab and associated assets will be
                // created), based on the drag-and-drop target in the Project
                // Browser.
                //
                // If the GLTF file(s) are dragged onto a directory, import into
                // that directory. If the GLTF file(s) are dragged onto a file,
                // import into the parent directory of that file.  If the GLTF
                // file(s) are dragged onto an empty area, import into the
                // directory that is currently selected in the left pane.

                string       dragTargetGuid = null;
                DropPosition dropPosition   = DropPosition.UponItem;
                for (int i = 0; i < _itemRectsLastFrame.Count; ++i)
                {
                    Rect   itemRect = _itemRectsLastFrame[i].rect;
                    string itemGuid = _itemRectsLastFrame[i].guid;

                    if (itemRect.Contains(globalMousePosition))
                    {
                        ProjectBrowserExtensions.ItemType itemType
                            = ProjectBrowserExtensions.GetItemType(itemRect);

                        string itemPath = UnityPathUtil.NormalizePathSeparators(
                            AssetDatabase.GUIDToAssetPath(itemGuid));

                        // TreeItem areas support dropping files between
                        // vertically adjacent folders/files (as indicated by a
                        // horizontal blue line in the UI), whereas
                        // list areas do not.
                        if (itemType == ProjectBrowserExtensions.ItemType.TreeItem)
                        {
                            // The height of the target region at the top/bottom of
                            // an item rect that corresponds to dropping files
                            // between items.
                            //
                            // This value is hardcoded to match Unity's internal
                            // value for `TreeViewGUI.k_HalfDropBetweenHeight`. For
                            // Unity's own implementation of this logic, see
                            // `TreeViewDragging.TryGetDropPosition`.
                            const float halfDropBetweenHeight = 4f;

                            if (globalMousePosition.y <= itemRect.yMin + halfDropBetweenHeight)
                            {
                                dropPosition = DropPosition.AboveItem;
                            }
                            else if (globalMousePosition.y >= itemRect.yMax - halfDropBetweenHeight)
                            {
                                dropPosition = DropPosition.BelowItem;
                            }
                            else
                            {
                                dropPosition = DropPosition.UponItem;
                            }
                        }

                        // The easy case: we are dropping file(s) directly onto a file/folder
                        // (rather than between two vertically adjacent files/folders).

                        if (dropPosition == DropPosition.UponItem)
                        {
                            dragTargetGuid = itemGuid;
                            break;
                        }

                        if (dropPosition == DropPosition.BelowItem)
                        {
                            // Special case: If we are dropping below the last item
                            // in a tree/list, then the drop target should be the
                            // parent folder of the target item.
                            //
                            // Note: In "Two Column Layout", `_itemRectsLastFrame`
                            // stores both tree items and list items in the same
                            // list. As a result, the current item (i) may be in
                            // the left pane (i.e. folder tree) while the next item
                            // (i + 1) is in the right pane (i.e. files list). In
                            // this case, we are not really dropping between items,
                            // but we are rather dropping after the last item in
                            // the folder tree.

                            ProjectBrowserExtensions.ItemType nextItemType
                                = ProjectBrowserExtensions.ItemType.TreeItem;

                            if (i + 1 < _itemRectsLastFrame.Count)
                            {
                                Rect nextItemRect = _itemRectsLastFrame[i + 1].rect;
                                nextItemType = ProjectBrowserExtensions
                                               .GetItemType(nextItemRect);
                            }

                            if (i + 1 >= _itemRectsLastFrame.Count ||
                                nextItemType != itemType)
                            {
                                // Special case: user is not allowed to drop into the
                                // parent folder of "Assets", so do nothing.
                                if (itemPath == "Assets")
                                {
                                    return;
                                }

                                string itemParentPath = Path.GetDirectoryName(itemPath);
                                dragTargetGuid = AssetDatabase.AssetPathToGUID(itemParentPath);

                                break;
                            }

                            // If the next item is a child file/folder of the
                            // current item, then the current item should be the
                            // drag target.  Otherwise, the current and next
                            // items are siblings and the drag target should
                            // be their shared parent folder.

                            string nextItemGuid = _itemRectsLastFrame[i + 1].guid;

                            string nextItemPath = UnityPathUtil.NormalizePathSeparators(
                                AssetDatabase.GUIDToAssetPath(nextItemGuid));

                            if (UnityPathUtil.GetParentDir(nextItemPath) == itemPath)
                            {
                                dragTargetGuid = itemGuid;
                                break;
                            }
                            else
                            {
                                dragTargetGuid = AssetDatabase.AssetPathToGUID(
                                    UnityPathUtil.GetParentDir(itemPath));
                                break;
                            }
                        }

                        if (dropPosition == DropPosition.AboveItem)
                        {
                            // If we are dropping above the topmost item in a
                            // tree/list.
                            //
                            // Note: This should never happen except when the user
                            // is drops files above the root "Assets" folder.  In
                            // the case that a subset of items is currently being
                            // shown in a scroll window, the tree/list window will
                            // automatically scroll up when the user hovers the
                            // mouse above the top visible item.

                            if (i == 0)
                            {
                                return;
                            }

                            // If the prev item is parent folder of the
                            // current item, then the previous item should be the
                            // drag target.  Otherwise, the current and previous
                            // items are siblings and the drag target should
                            // be their shared parent folder.

                            string prevItemGuid = _itemRectsLastFrame[i - 1].guid;

                            string prevItemPath = UnityPathUtil.NormalizePathSeparators(
                                AssetDatabase.GUIDToAssetPath(prevItemGuid));

                            if (UnityPathUtil.GetParentDir(itemPath) == prevItemPath)
                            {
                                dragTargetGuid = prevItemGuid;
                                break;
                            }
                            else
                            {
                                dragTargetGuid = AssetDatabase.AssetPathToGUID(
                                    UnityPathUtil.GetParentDir(itemPath));
                                break;
                            }
                        }
                    }
                }

                if (dragTargetGuid == null)
                {
                    // The user dragged the GLTF file(s) onto an empty area of the
                    // Project Browser. Use the currently selected folder in the
                    // left pane (i.e. folder tree) as the import directory.

                    dragTargetGuid = AssetDatabase.AssetPathToGUID(
                        ProjectBrowserExtensions.GetSelectedProjectFolder());
                }

                string dragTargetProjectPath = AssetDatabase.GUIDToAssetPath(dragTargetGuid);
                string dragTargetPath        = UnityPathUtil.GetAbsolutePath(dragTargetProjectPath);

                // Invoke user-defined drag-and-drop callbacks

                OnDragAndDrop?.Invoke(dragTargetPath, DragAndDrop.paths);
            }
        }
Пример #11
0
        /// <summary>
        /// Callback that is invoked when external file(s) are
        /// dragged-and-dropped into the Project Browser.
        /// </summary>
        private static void HandleDragAndDrop(string targetPath, string[] droppedPaths)
        {
            // Hold the Control key or Command key while
            // dragging-and-dropping a .gltf/.glb/.zip to
            // copy the file into the project without
            // performing an automatic glTF import.

            if (Event.current.control || Event.current.command)
            {
                return;
            }

            // Read current import options from Piglet Options window.
            //
            // Note: The SaveAssets/Refresh calls ensure that any changes
            // made in the Piglet Options window are saved out to disk
            // first.

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            _pigletOptions = Resources.Load <PigletOptions>("PigletOptions");

            // Do nothing if drag-and-drop glTF import has been disabled by the user

            if (!_pigletOptions.EnableDragAndDropImport)
            {
                return;
            }

            // If `targetPath` is a regular file and not a directory, use
            // the parent directory as the import directory.

            string importDir = Directory.Exists(targetPath)
                ? targetPath : Path.GetDirectoryName(targetPath);

            importDir = UnityPathUtil.NormalizePathSeparators(importDir);

            // Exclude files that don't have .gltf/.glb extension.
            //
            // Note: I would prefer to pass skipped files through to Unity
            // for default drag-and-drop handling, but that does not seem
            // to be possible because `DragAndDrop.paths` is read-only.

            List <string> acceptedPaths = new List <string>();

            foreach (string path in DragAndDrop.paths)
            {
                // Don't trigger automatic glTF import when we are dragging
                // a .gltf/.glb/.zip file from within the Unity project folder.
                //
                // When the source file is inside the Unity project folder,
                // the Unity drag-and-drop machinery will report a relative path
                // starting with "Assets/".

                if (path.StartsWith("Assets/"))
                {
                    continue;
                }

                string _path = path.ToLower();

                if (_path.EndsWith(".gltf") || _path.EndsWith(".glb"))
                {
                    acceptedPaths.Add(path);
                }

                else if (_path.EndsWith(".zip") && ZipUtil.ContainsGltfFile(path))
                {
                    acceptedPaths.Add(path);
                }
            }

            if (acceptedPaths.Count > 0)
            {
                // Run GLTF import(s) in the background.

                StartImport(acceptedPaths, importDir);

                // Consume the `DragPerform` event, so that Unity's
                // default drag-and-drop handling, which
                // simply copies the file(s) into the target Assets
                // folder, is not performed.

                Event.current.Use();
            }
        }
Пример #12
0
        /// <summary>
        /// Coroutine to import GLTF files with Piglet's EditorGltfImporter.
        /// The string value returned via the IEnumerator is the target directory
        /// for the current import, so that files from an aborted/canceled import
        /// can be easily cleaned up.
        /// </summary>
        private static IEnumerator <string> ImportCoroutine(List <string> gltfPaths, string baseImportDir)
        {
            foreach (string gltfPath in gltfPaths)
            {
                string gltfBasename      = Path.GetFileName(gltfPath);
                string gltfBasenameNoExt = Path.GetFileNameWithoutExtension(gltfPath);

                bool abortImport = false;

                // callback for updating progress during glTF import
                void OnProgress(GltfImportStep type, int count, int total)
                {
                    ProgressLog.Instance.OnImportProgress(type, count, total);

                    abortImport = EditorUtility.DisplayCancelableProgressBar(
                        $"Importing {gltfBasename}...",
                        ProgressLog.Instance.GetProgressMessage(),
                        (float)count / total);
                }

                string importPath = UnityPathUtil.NormalizePathSeparators(
                    Path.Combine(baseImportDir, gltfBasenameNoExt));
                string importProjectPath = UnityPathUtil.GetProjectPath(importPath);

                if ((Directory.Exists(importPath) || File.Exists(importPath)) &&
                    _pigletOptions.PromptBeforeOverwritingFiles)
                {
                    if (!EditorUtility.DisplayDialog(
                            "Warning!",
                            $"Overwrite \"{importProjectPath}\"?",
                            "OK", "Cancel"))
                    {
                        yield break;
                    }

                    FileUtil.DeleteFileOrDirectory(importPath);
                    AssetDatabase.Refresh();
                }

                GltfImportTask importTask =
                    EditorGltfImporter.GetImportTask(gltfPath, importPath,
                                                     _pigletOptions.ImportOptions);

                importTask.OnProgress = OnProgress;

                GameObject importedPrefab = null;
                importTask.OnCompleted = (prefab) => importedPrefab = prefab;

                // restart import timer at zero
                ProgressLog.Instance.StartImport();

                while (true)
                {
                    if (abortImport)
                    {
                        importTask.Abort();
                        EditorUtility.ClearProgressBar();
                        yield break;
                    }

                    try
                    {
                        if (!importTask.MoveNext())
                        {
                            break;
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogException(e);

                        EditorUtility.ClearProgressBar();
                        EditorUtility.DisplayDialog("Import Failed",
                                                    String.Format("Import of {0} failed. "
                                                                  + "See Unity console log for details.", gltfBasename),
                                                    "OK");

                        yield break;
                    }

                    yield return(importPath);
                }

                // Before modifying the selection, store a handle to
                // the transform of the currently selected game object (if any).

                Transform selectedTransform = Selection.activeTransform;

                // Select the prefab file in the Project Browser.
                if (_pigletOptions.SelectPrefabAfterImport)
                {
                    Selection.activeObject = importedPrefab;
                    yield return(importPath);
                }

                if (_pigletOptions.AddPrefabToScene)
                {
                    // If we are currently in Prefab Mode, exit
                    // back to the main scene hierarchy view.
                    //
                    // Note: Prefab Mode was introduced in Unity 2018.3.
#if UNITY_2018_3_OR_NEWER
                    if (StageUtility.GetCurrentStageHandle()
                        != StageUtility.GetMainStageHandle())
                    {
                        StageUtility.GoToMainStage();
                    }
#endif

                    GameObject instance = (GameObject)PrefabUtility
                                          .InstantiatePrefab(importedPrefab);

                    // parent the prefab instance to the currently
                    // selected GameObject (if any)
                    if (selectedTransform != null)
                    {
                        instance.transform.parent = selectedTransform;
                    }

                    if (_pigletOptions.SelectPrefabInScene)
                    {
                        Selection.activeGameObject = instance;
                        yield return(importPath);
                    }
                }

                if (_pigletOptions.OpenPrefabAfterImport)
                {
                    AssetDatabase.OpenAsset(importedPrefab);

                    // Note: This is the best method I could find
                    // for automatically centering the prefab in
                    // the scene view. For further info, see
                    // https://answers.unity.com/questions/813814/framing-objects-via-script-in-the-unity-editor.html
                    SceneView.FrameLastActiveSceneView();
                }

                EditorUtility.ClearProgressBar();
            }
        }