/// <summary> /// Reimports the texture resource according to the currently set import options. /// </summary> private void TriggerReimport() { Texture texture = (Texture)InspectedObject; string resourcePath = ProjectLibrary.GetPath(texture); ProjectLibrary.Reimport(resourcePath, importOptions, true); }
/// <summary> /// Checks the current state of import and updates the GUI elements accordingly. /// </summary> internal void Update() { bool isImporting = ProjectLibrary.GetImportProgress(path) < 1.0f; guiSpinner.Active = isImporting; reimportButton.Active = !isImporting; }
/// <summary> /// Triggered when the user double-clicked on the entry. /// </summary> /// <param name="path">Project library path of the double-clicked entry.</param> private void OnEntryDoubleClicked(string path) { delayedSelect = false; LibraryEntry entry = ProjectLibrary.GetEntry(path); if (entry != null) { if (entry.Type == LibraryEntryType.Directory) { owner.Window.EnterDirectory(path); } else { ResourceMeta meta = ProjectLibrary.GetMeta(path); FileEntry fileEntry = (FileEntry)entry; if (meta.ResType == ResourceType.Prefab) { EditorApplication.LoadScene(fileEntry.Path); } else if (meta.ResType == ResourceType.ScriptCode) { ProgressBar.Show("Opening external code editor...", 1.0f); delayedOpenCodeEditorFrame = Time.FrameIdx + 1; } else if (meta.ResType == ResourceType.PlainText || meta.ResType == ResourceType.Shader || meta.ResType == ResourceType.ShaderInclude) { string absPath = Path.Combine(ProjectLibrary.ResourceFolder, fileEntry.Path); Process.Start(absPath); } } } }
/// <summary> /// Retrieves import options for the resource we're currently inspecting. /// </summary> /// <returns>Script code import options object.</returns> private ScriptCodeImportOptions GetImportOptions() { ScriptCode scriptCode = InspectedObject as ScriptCode; ScriptCodeImportOptions output = null; if (scriptCode != null) { LibraryEntry libEntry = ProjectLibrary.GetEntry(ProjectLibrary.GetPath(scriptCode)); if (libEntry != null && libEntry.Type == LibraryEntryType.File) { FileEntry fileEntry = (FileEntry)libEntry; output = fileEntry.Options as ScriptCodeImportOptions; } } if (output == null) { if (importOptions == null) { output = new ScriptCodeImportOptions(); } else { output = importOptions; } } return(output); }
/// <summary> /// Loads the currently inspected resource into the <see cref="InspectedObject"/> field. By default resources /// are not loaded and you can only retrieve their path through <see cref="InspectedResourcePath"/>. /// </summary> protected void LoadResource() { if (!string.IsNullOrEmpty(inspectedResourcePath)) { inspectedObject = ProjectLibrary.Load <Resource>(inspectedResourcePath); } }
/// <summary> /// Retrieves import options for the texture we're currently inspecting. /// </summary> /// <returns>Texture import options object.</returns> private TextureImportOptions GetImportOptions() { TextureImportOptions output = null; LibraryEntry texEntry = ProjectLibrary.GetEntry(InspectedResourcePath); if (texEntry != null && texEntry.Type == LibraryEntryType.File) { FileEntry texFileEntry = (FileEntry)texEntry; output = texFileEntry.Options as TextureImportOptions; } if (output == null) { if (importOptions == null) { output = new TextureImportOptions(); } else { output = importOptions; } } return(output); }
/// <summary> /// Retrieves import options for the audio clip we're currently inspecting. /// </summary> /// <returns>Audio clip import options object.</returns> private AudioClipImportOptions GetImportOptions() { AudioClipImportOptions output = null; LibraryEntry meshEntry = ProjectLibrary.GetEntry(InspectedResourcePath); if (meshEntry != null && meshEntry.Type == LibraryEntryType.File) { FileEntry meshFileEntry = (FileEntry)meshEntry; output = meshFileEntry.Options as AudioClipImportOptions; } if (output == null) { if (importOptions == null) { output = new AudioClipImportOptions(); } else { output = importOptions; } } return(output); }
/// <summary> /// Checks if a file or folder at the specified path exists in the library, and if it does generates a new unique /// name for the file or folder. /// </summary> /// <param name="path">Path to the file or folder to generate a unique name.</param> /// <returns>New path with the unique name. This will be unchanged from input path if the input path doesn't /// already exist.</returns> public static string GetUniquePath(string path) { string extension = Path.GetExtension(path); string pathClean = path; if (!String.IsNullOrEmpty(extension)) { pathClean = path.Remove(path.Length - extension.Length); } int idx = 0; int separatorIdx = pathClean.LastIndexOf('_'); if (separatorIdx != -1) { string numberString = pathClean.Substring(separatorIdx + 1, pathClean.Length - (separatorIdx + 1)); if (int.TryParse(numberString, out idx)) { pathClean = pathClean.Substring(0, separatorIdx); idx++; } } string destination = path; while (ProjectLibrary.Exists(destination)) { destination = pathClean + "_" + idx + extension; idx++; } return(destination); }
/// <inheritdoc/> protected internal override void Initialize() { importOptions = GetImportOptions(); formatField.OnSelectionChanged += x => importOptions.Format = (PixelFormat)x; generateMipsField.OnChanged += x => importOptions.GenerateMips = x; maximumMipsField.OnChanged += x => importOptions.MaxMip = x; srgbField.OnChanged += x => importOptions.SRGB = x; cpuCachedField.OnChanged += x => importOptions.CpuCached = x; isCubemapField.OnChanged += x => importOptions.Cubemap = x; cubemapSourceTypeField.OnSelectionChanged += x => importOptions.CubemapSourceType = (CubemapSourceType)x; Layout.AddElement(formatField); Layout.AddElement(generateMipsField); Layout.AddElement(maximumMipsField); Layout.AddElement(srgbField); Layout.AddElement(cpuCachedField); Layout.AddElement(isCubemapField); Layout.AddElement(cubemapSourceTypeField); Layout.AddSpace(10); reimportButton = new GUIReimportButton(InspectedResourcePath, Layout, () => { ProjectLibrary.Reimport(InspectedResourcePath, importOptions, true); }); UpdateGUIValues(); }
public static void SaveProject() { // Apply changes to any animation clips edited using the animation editor foreach (var KVP in persistentData.dirtyAnimClips) { KVP.Value.SaveToClip(); } // Save all dirty resources to disk foreach (var KVP in persistentData.dirtyResources) { UUID resourceUUID = KVP.Key; string path = ProjectLibrary.GetPath(resourceUUID); if (!IsNative(path)) { continue; // Imported resources can't be changed } Resource resource = ProjectLibrary.Load <Resource>(path); if (resource != null) { ProjectLibrary.Save(resource); } } persistentData.dirtyAnimClips.Clear(); persistentData.dirtyResources.Clear(); SetStatusProject(false); Internal_SaveProject(); }
/// <summary> /// Opens a dialog to allows the user to select a location where to save the current scene. If scene was previously /// saved it is instead automatically saved at the last location. /// </summary> public static void SaveScene(Action onSuccess = null, Action onFailure = null) { if (!Scene.ActiveSceneUUID.IsEmpty()) { string scenePath = ProjectLibrary.GetPath(Scene.ActiveSceneUUID); if (!string.IsNullOrEmpty(scenePath)) { if (Scene.IsGenericPrefab) { SaveGenericPrefab(onSuccess, onFailure); } else { SaveScene(scenePath); if (onSuccess != null) { onSuccess(); } } } else { SaveSceneAs(onSuccess, onFailure); } } else { SaveSceneAs(onSuccess, onFailure); } }
/// <summary> /// Reimports the script code resource according to the currently set import options. /// </summary> private void TriggerReimport() { ScriptCode scriptCode = (ScriptCode)InspectedObject; string resourcePath = ProjectLibrary.GetPath(scriptCode); ProjectLibrary.Reimport(resourcePath, importOptions, true); }
/// <summary> /// Creates a new folder with in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the new folder in.</param> public static void CreateFolder(string folder) { string path = Path.Combine(folder, "New Folder"); path = GetUniquePath(path); ProjectLibrary.CreateFolder(path); }
/// <summary> /// Triggered by the runtime when a resource is dropped on the scene tree view. /// </summary> private void Internal_DoOnResourceDropped(SceneObject parent, string[] resourcePaths) { if (resourcePaths == null) { return; } List <SceneObject> addedObjects = new List <SceneObject>(); for (int i = 0; i < resourcePaths.Length; i++) { ResourceMeta meta = ProjectLibrary.GetMeta(resourcePaths[i]); if (meta == null) { continue; } if (meta.ResType == ResourceType.Mesh) { if (!string.IsNullOrEmpty(resourcePaths[i])) { string meshName = Path.GetFileNameWithoutExtension(resourcePaths[i]); Mesh mesh = ProjectLibrary.Load <Mesh>(resourcePaths[i]); if (mesh == null) { continue; } SceneObject so = UndoRedo.CreateSO(meshName, "Created a new Renderable \"" + meshName + "\""); so.Parent = parent; Renderable renderable = so.AddComponent <Renderable>(); renderable.Mesh = mesh; addedObjects.Add(so); } } else if (meta.ResType == ResourceType.Prefab) { if (!string.IsNullOrEmpty(resourcePaths[i])) { Prefab prefab = ProjectLibrary.Load <Prefab>(resourcePaths[i]); SceneObject so = UndoRedo.Instantiate(prefab, "Instantiating " + prefab.Name); so.Parent = parent; addedObjects.Add(so); } } } if (addedObjects.Count > 0) { EditorApplication.SetSceneDirty(); } Selection.SceneObjects = addedObjects.ToArray(); }
/// <summary> /// Saves the currently loaded scene to the specified path. /// </summary> /// <param name="path">Path relative to the resource folder. This can be the path to the existing scene /// prefab if it just needs updating. </param> internal static void SaveScene(string path) { Prefab scene = Internal_SaveScene(path); Scene.SetActive(scene); ProjectLibrary.Refresh(true); SetSceneDirty(false); }
/// <summary> /// Recreates all the GUI elements used by this inspector. /// </summary> private void BuildGUI() { Layout.Clear(); normalsField = new GUIToggleField(new LocEdString("Import Normals")); tangentsField = new GUIToggleField(new LocEdString("Import Tangents")); skinField = new GUIToggleField(new LocEdString("Import Skin")); blendShapesField = new GUIToggleField(new LocEdString("Import Blend Shapes")); animationField = new GUIToggleField(new LocEdString("Import Animation")); scaleField = new GUIFloatField(new LocEdString("Scale")); cpuCachedField = new GUIToggleField(new LocEdString("CPU cached")); collisionMeshTypeField = new GUIEnumField(typeof(CollisionMeshType), new LocEdString("Collision mesh")); keyFrameReductionField = new GUIToggleField(new LocEdString("Keyframe Reduction")); rootMotionField = new GUIToggleField(new LocEdString("Import root motion")); normalsField.OnChanged += x => importOptions.ImportNormals = x; tangentsField.OnChanged += x => importOptions.ImportTangents = x; skinField.OnChanged += x => importOptions.ImportSkin = x; blendShapesField.OnChanged += x => importOptions.ImportBlendShapes = x; animationField.OnChanged += x => importOptions.ImportAnimation = x; scaleField.OnChanged += x => importOptions.ImportScale = x; cpuCachedField.OnChanged += x => importOptions.CpuCached = x; collisionMeshTypeField.OnSelectionChanged += x => importOptions.CollisionMeshType = (CollisionMeshType)x; keyFrameReductionField.OnChanged += x => importOptions.ReduceKeyFrames = x; rootMotionField.OnChanged += x => importOptions.ImportRootMotion = x; Layout.AddElement(normalsField); Layout.AddElement(tangentsField); Layout.AddElement(skinField); Layout.AddElement(blendShapesField); Layout.AddElement(animationField); Layout.AddElement(scaleField); Layout.AddElement(cpuCachedField); Layout.AddElement(collisionMeshTypeField); Layout.AddElement(keyFrameReductionField); Layout.AddElement(rootMotionField); splitInfos = importOptions.AnimationSplits; animSplitInfoField = GUIArrayField <AnimationSplitInfo, AnimSplitArrayRow> .Create( new LocEdString("Animation splits"), splitInfos, Layout); animSplitInfoField.OnChanged += x => { splitInfos = x; }; animSplitInfoField.IsExpanded = Persistent.GetBool("animSplitInfos_Expanded"); animSplitInfoField.OnExpand += x => Persistent.SetBool("animSplitInfos_Expanded", x); Layout.AddSpace(10); reimportButton = new GUIReimportButton(InspectedResourcePath, Layout, () => { importOptions.AnimationSplits = splitInfos; ProjectLibrary.Reimport(InspectedResourcePath, importOptions, true); }); UpdateGUIValues(); }
/// <summary> /// Creates a new physics material with the default properties in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the material in.</param> public static void CreateEmptyPhysicsMaterial(string folder) { string path = Path.Combine(folder, "New Physics Material.asset"); path = GetUniquePath(path); PhysicsMaterial material = new PhysicsMaterial(); ProjectLibrary.Create(material, path); }
/// <summary> /// Creates a new shader containing a rough code outline in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the shader in.</param> public static void CreateEmptyShader(string folder) { string path = Path.Combine(folder, "New Shader.bsl"); path = Path.Combine(ProjectLibrary.ResourceFolder, path); path = GetUniquePath(path); File.WriteAllText(path, EditorBuiltin.EmptyShaderCode); ProjectLibrary.Refresh(path); }
/// <summary> /// Creates a new empty GUI skin in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the GUI skin in.</param> public static void CreateEmptyGUISkin(string folder) { string path = Path.Combine(folder, "New GUI Skin.asset"); path = GetUniquePath(path); GUISkin guiSkin = new GUISkin(); ProjectLibrary.Create(guiSkin, path); }
/// <summary> /// Creates a new empty string table in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the string table in.</param> public static void CreateEmptyStringTable(string folder) { string path = Path.Combine(folder, "New String Table.asset"); path = GetUniquePath(path); StringTable stringTable = new StringTable(); ProjectLibrary.Create(stringTable, path); }
/// <summary> /// Creates a new empty sprite texture in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the sprite texture in.</param> public static void CreateEmptySpriteTexture(string folder) { string path = Path.Combine(folder, "New Sprite Texture.asset"); path = GetUniquePath(path); SpriteTexture spriteTexture = new SpriteTexture(null); ProjectLibrary.Create(spriteTexture, path); }
/// <summary> /// Creates a new material with the default shader in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the material in.</param> public static void CreateEmptyMaterial(string folder) { string path = Path.Combine(folder, "New Material.asset"); path = GetUniquePath(path); Material material = new Material(Builtin.GetShader(BuiltinShader.Standard)); ProjectLibrary.Create(material, path); }
/// <summary> /// Constructs a new script code manager. /// </summary> internal ScriptCodeManager() { ProjectLibrary.OnEntryAdded += OnEntryAdded; ProjectLibrary.OnEntryRemoved += OnEntryRemoved; ProjectLibrary.OnEntryImported += OnEntryImported; // Check for missing or out of date assemblies DateTime lastModifiedGameScript = DateTime.MinValue; DateTime lastModifiedEditorScript = DateTime.MinValue; LibraryEntry[] scriptEntries = ProjectLibrary.Search("*.cs", new ResourceType[] { ResourceType.ScriptCode }); for (int i = 0; i < scriptEntries.Length; i++) { if (scriptEntries[i].Type != LibraryEntryType.File) { continue; } FileEntry fileEntry = (FileEntry)scriptEntries[i]; string absPath = Path.Combine(ProjectLibrary.ResourceFolder, fileEntry.Path); ScriptCodeImportOptions io = (ScriptCodeImportOptions)fileEntry.Options; if (io.EditorScript) { lastModifiedEditorScript = File.GetLastWriteTime(absPath); } else { lastModifiedGameScript = File.GetLastWriteTime(absPath); } } DateTime lastCompileTime = new DateTime(EditorApplication.PersistentData.lastCompileTime); if (lastModifiedGameScript != DateTime.MinValue) { string gameAssemblyPath = Path.Combine(EditorApplication.ScriptAssemblyPath, EditorApplication.ScriptGameAssemblyName); isGameAssemblyDirty = (!File.Exists(gameAssemblyPath) || File.GetLastWriteTime(gameAssemblyPath) < lastModifiedGameScript) && (lastModifiedGameScript > lastCompileTime); } if (lastModifiedEditorScript != DateTime.MinValue) { string editorAssemblyPath = Path.Combine(EditorApplication.ScriptAssemblyPath, EditorApplication.ScriptEditorAssemblyName); isEditorAssemblyDirty = (!File.Exists(editorAssemblyPath) || File.GetLastWriteTime(editorAssemblyPath) < lastModifiedEditorScript) && (lastModifiedEditorScript > lastCompileTime); } }
/// <summary> /// Creates a new C# script containing a rough code outline in the specified folder. /// </summary> /// <param name="folder">Folder relative to project library to create the C# script in.</param> /// <returns>The path of the created resource.</returns> public static string CreateEmptyCSScript(string folder) { string path = Path.Combine(folder, "NewScript.cs"); path = GetUniquePath(path); string filePath = Path.Combine(ProjectLibrary.ResourceFolder, path); File.WriteAllText(filePath, EditorBuiltin.EmptyCSScriptCode); ProjectLibrary.Refresh(filePath); return(path); }
/// <summary> /// Saves the animation curves and events stored in this object, into the associated animation clip resource. /// Relevant animation clip resource must already be created and exist in the project library. /// </summary> public void SaveToClip() { if (!isImported) { EditorAnimClipData editorAnimClipData; Apply(out editorAnimClipData); string resourcePath = ProjectLibrary.GetPath(clip); ProjectLibrary.Save(clip); ProjectLibrary.SetEditorData(resourcePath, editorAnimClipData); } else { string resourcePath = ProjectLibrary.GetPath(clip); LibraryEntry entry = ProjectLibrary.GetEntry(resourcePath); if (entry != null && entry.Type == LibraryEntryType.File) { FileEntry fileEntry = (FileEntry)entry; MeshImportOptions meshImportOptions = (MeshImportOptions)fileEntry.Options; string clipName = PathEx.GetTail(resourcePath); List <ImportedAnimationEvents> newEvents = new List <ImportedAnimationEvents>(); newEvents.AddRange(meshImportOptions.AnimationEvents); bool isExisting = false; for (int i = 0; i < newEvents.Count; i++) { if (newEvents[i].Name == clipName) { newEvents[i].Events = events; isExisting = true; break; } } if (!isExisting) { ImportedAnimationEvents newEntry = new ImportedAnimationEvents(); newEntry.Name = clipName; newEntry.Events = events; newEvents.Add(newEntry); } meshImportOptions.AnimationEvents = newEvents.ToArray(); ProjectLibrary.Reimport(resourcePath, meshImportOptions, true); } } }
/// <summary> /// Attempts to save the current scene by applying the changes to a prefab, instead of saving it as a brand new /// scene. This is necessary for generic prefabs that have don't have a scene root included in the prefab. If the /// object added any other objects to the root, or has moved or deleted the original generic prefab the user /// will be asked to save the scene normally, creating a brand new prefab. /// </summary> private static void SaveGenericPrefab(Action onSuccess = null, Action onFailure = null) { // Find prefab root SceneObject root = null; int numChildren = Scene.Root.GetNumChildren(); int numNormalChildren = 0; for (int i = 0; i < numChildren; i++) { SceneObject child = Scene.Root.GetChild(i); if (EditorUtility.IsInternal(child)) { continue; } UUID prefabUUID = PrefabUtility.GetPrefabUUID(child); if (prefabUUID == Scene.ActiveSceneUUID) { root = child; } // If user added any other prefabs other than the initial one, the scene no longer represents a generic // prefab (as we can now longer save it by applying changes only to that prefab) numNormalChildren++; if (numNormalChildren > 1) { root = null; break; } } if (root != null) { PrefabUtility.ApplyPrefab(root, false); ProjectLibrary.Refresh(true); SetSceneDirty(false); if (onSuccess != null) { onSuccess(); } } else { SaveSceneAs(onSuccess, onFailure); } }
/// <summary> /// Creates a new material parameter GUI. /// </summary> /// <param name="shaderParam">Shader parameter to create the GUI for. Must be of texture type.</param> /// <param name="material">Material the parameter is a part of.</param> /// <param name="layout">Layout to append the GUI elements to.</param> internal MaterialParamTextureGUI(ShaderParameter shaderParam, Material material, GUILayout layout) : base(shaderParam) { LocString title = new LocEdString(shaderParam.name); GUITextureFieldType type = shaderParam.type == ShaderParameterType.Texture2D ? GUITextureFieldType.TextureOrSpriteTexture : GUITextureFieldType.Texture; guiElem = new GUITextureField(type, title); switch (shaderParam.type) { case ShaderParameterType.Texture2D: case ShaderParameterType.Texture3D: case ShaderParameterType.TextureCube: guiElem.OnChanged += (x) => { string path = ProjectLibrary.GetPath(x.UUID); if (!string.IsNullOrEmpty(path)) { if (ProjectLibrary.GetEntry(path) is FileEntry fileEntry) { if (fileEntry.ResourceMetas.Length > 0) { ResourceMeta meta = fileEntry.ResourceMetas[0]; if (meta.ResType == ResourceType.SpriteTexture) { material.SetSpriteTexture(shaderParam.name, x.As <SpriteTexture>()); } else if (meta.ResType == ResourceType.Texture) { material.SetTexture(shaderParam.name, x.As <Texture>()); } } } } else { material.SetTexture(shaderParam.name, null); } EditorApplication.SetDirty(material); }; break; } layout.AddElement(guiElem); }
/// <summary> /// Called every frame by the runtime. /// </summary> internal void OnEditorUpdate() { // Update managers ProjectLibrary.Update(); codeManager.Update(); if (delayUnloadProject) { delayUnloadProject = false; UnloadProject(); delayUnloadCallback?.Invoke(); delayUnloadCallback = null; } }
/// <summary> /// Creates the reimport GUI elements in the provided layout. /// </summary> /// <param name="path">Path of the resource that can be reimported.</param> /// <param name="parent">Parent GUI layout to which to add the reimport GUI elements.</param> /// <param name="doReimport">User provided callback that triggers when the reimport button is clicked.</param> internal GUIReimportButton(string path, GUILayout parent, Action doReimport) { this.path = path; reimportButton.OnClick += () => doReimport(); GUILayout reimportButtonLayout = parent.AddLayoutX(); reimportButtonLayout.AddFlexibleSpace(); reimportButtonLayout.AddElement(reimportButton); reimportButtonLayout.AddElement(guiSpinner); bool isImporting = ProjectLibrary.GetImportProgress(path) < 1.0f; guiSpinner.Active = isImporting; reimportButton.Active = !isImporting; }
/// <summary> /// Triggered when a resource is (re)imported in the project library. /// </summary> /// <param name="path">Path of the imported resource, relative to the project's resource folder.</param> private void OnEntryImported(string path) { LibraryEntry entry = ProjectLibrary.GetEntry(path); if (entry == null || entry.Type != LibraryEntryType.File) { return; } FileEntry fileEntry = (FileEntry)entry; ResourceMeta[] resourceMetas = fileEntry.ResourceMetas; bool found = false; foreach (var meta in resourceMetas) { if (meta.ResType == ResourceType.ScriptCode) { found = true; break; } } if (!found) { return; } ScriptCode codeFile = ProjectLibrary.Load <ScriptCode>(path); if (codeFile == null) { return; } if (codeFile.EditorScript) { isEditorAssemblyDirty = true; } else { isGameAssemblyDirty = true; } }