12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556 |
- using System;
- using System.IO;
- using UnityEditorInternal;
- using UnityEngine;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Runtime.Serialization;
- using System.Security.Permissions;
- namespace UnityEditor.Formats.Fbx.Exporter {
- [System.Serializable]
- internal class FbxExportSettingsException : System.Exception
- {
- public FbxExportSettingsException() { }
- public FbxExportSettingsException(string message)
- : base(message) { }
- public FbxExportSettingsException(string message, System.Exception inner)
- : base(message, inner) { }
- protected FbxExportSettingsException(SerializationInfo info, StreamingContext context)
- : base(info, context) { }
- }
- [CustomEditor(typeof(ExportSettings))]
- internal class ExportSettingsEditor : UnityEditor.Editor {
- Vector2 scrollPos = Vector2.zero;
- const float LabelWidth = 144;
- const float SelectableLabelMinWidth = 90;
- const float BrowseButtonWidth = 25;
- const float FieldOffset = 18;
- const float BrowseButtonOffset = 5;
- static class Style
- {
- public static GUIContent Application3D = new GUIContent(
- "3D Application:",
- "Select the 3D Application for which you would like to install the Unity integration.");
- public static GUIContent KeepOpen = new GUIContent("Keep Open:",
- "Keep the selected 3D application open after Unity integration install has completed.");
- public static GUIContent HideNativeMenu = new GUIContent("Hide Native Menu:",
- "Replace Maya's native 'Send to Unity' menu with the Unity Integration's menu");
- public static GUIContent InstallIntegrationContent = new GUIContent(
- "Install Unity Integration",
- "Install and configure the Unity integration for the selected 3D application so that you can import and export directly with this project.");
- public static GUIContent RepairMissingScripts = new GUIContent(
- "Run Component Updater",
- "If FBX exporter version 1.3.0f1 or earlier was previously installed, then links to the FbxPrefab component will need updating.\n" +
- "Run this to update all FbxPrefab references in text serialized prefabs and scene files.");
- }
- [SecurityPermission(SecurityAction.LinkDemand)]
- public override void OnInspectorGUI() {
- ExportSettings exportSettings = (ExportSettings)target;
- // Increasing the label width so that none of the text gets cut off
- EditorGUIUtility.labelWidth = LabelWidth;
- scrollPos = GUILayout.BeginScrollView (scrollPos);
- var version = UnityEditor.Formats.Fbx.Exporter.ModelExporter.GetVersionFromReadme ();
- if (!string.IsNullOrEmpty(version)) {
- GUILayout.Label ("Version: " + version, EditorStyles.centeredGreyMiniLabel);
- EditorGUILayout.Space ();
- }
- GUILayout.BeginVertical();
- EditorGUILayout.LabelField("Export Options", EditorStyles.boldLabel);
- EditorGUI.indentLevel++;
- exportSettings.ShowConvertToPrefabDialog = EditorGUILayout.Toggle(
- new GUIContent("Show Convert UI:", "Show the Convert dialog when converting to an FBX Prefab Variant"),
- exportSettings.ShowConvertToPrefabDialog
- );
- EditorGUILayout.Space();
- EditorGUILayout.Space();
- EditorGUI.indentLevel--;
- EditorGUILayout.LabelField("Integration", EditorStyles.boldLabel);
- EditorGUI.indentLevel++;
- GUILayout.BeginHorizontal ();
- EditorGUILayout.LabelField(Style.Application3D, GUILayout.Width(LabelWidth - FieldOffset));
-
- // dropdown to select Maya version to use
- var options = ExportSettings.GetDCCOptions();
- exportSettings.SelectedDCCApp = EditorGUILayout.Popup(exportSettings.SelectedDCCApp, options);
- if (GUILayout.Button(new GUIContent("...", "Browse to a 3D application in a non-default location"), EditorStyles.miniButton, GUILayout.Width(BrowseButtonWidth))) {
- var ext = "";
- switch (Application.platform) {
- case RuntimePlatform.WindowsEditor:
- ext = "exe";
- break;
- case RuntimePlatform.OSXEditor:
- ext = "app";
- break;
- default:
- throw new System.NotImplementedException ();
- }
- string dccPath = EditorUtility.OpenFilePanel ("Select Digital Content Creation Application", ExportSettings.FirstValidVendorLocation, ext);
- // check that the path is valid and references the maya executable
- if (!string.IsNullOrEmpty (dccPath)) {
- ExportSettings.DCCType foundDCC = ExportSettings.DCCType.Maya;
- var foundDCCPath = TryFindDCC (dccPath, ext, ExportSettings.DCCType.Maya);
- if (foundDCCPath == null && Application.platform == RuntimePlatform.WindowsEditor) {
- foundDCCPath = TryFindDCC (dccPath, ext, ExportSettings.DCCType.Max);
- foundDCC = ExportSettings.DCCType.Max;
- }
- if (foundDCCPath == null) {
- Debug.LogError (string.Format ("Could not find supported 3D application at: \"{0}\"", Path.GetDirectoryName (dccPath)));
- } else {
- dccPath = foundDCCPath;
- ExportSettings.AddDCCOption (dccPath, foundDCC);
- }
- Repaint ();
- }
- }
- GUILayout.EndHorizontal ();
- EditorGUILayout.Space();
- exportSettings.LaunchAfterInstallation = EditorGUILayout.Toggle(
- Style.KeepOpen,
- exportSettings.LaunchAfterInstallation
- );
- exportSettings.HideSendToUnityMenuProperty = EditorGUILayout.Toggle(
- Style.HideNativeMenu,
- exportSettings.HideSendToUnityMenuProperty
- );
- EditorGUILayout.Space();
- // disable button if no 3D application is available
- EditorGUI.BeginDisabledGroup (!ExportSettings.CanInstall());
- if (GUILayout.Button (Style.InstallIntegrationContent)) {
- EditorApplication.delayCall += UnityEditor.Formats.Fbx.Exporter.IntegrationsUI.InstallDCCIntegration;
- }
- EditorGUI.EndDisabledGroup ();
- EditorGUILayout.Space ();
- EditorGUI.indentLevel--;
- EditorGUILayout.LabelField ("FBX Prefab Component Updater", EditorStyles.boldLabel);
- EditorGUI.indentLevel++;
- EditorGUILayout.Space ();
-
- if (GUILayout.Button (Style.RepairMissingScripts)) {
- var componentUpdater = new UnityEditor.Formats.Fbx.Exporter.RepairMissingScripts ();
- var filesToRepairCount = componentUpdater.AssetsToRepairCount;
- var dialogTitle = "FBX Prefab Component Updater";
- if (filesToRepairCount > 0) {
- bool result = UnityEditor.EditorUtility.DisplayDialog (dialogTitle,
- string.Format("Found {0} prefab(s) and/or scene(s) with components requiring update.\n\n" +
- "If you choose 'Go Ahead', the FbxPrefab components in these assets " +
- "will be automatically updated to work with the latest FBX exporter.\n" +
- "You should make a backup before proceeding.", filesToRepairCount),
- "I Made a Backup. Go Ahead!", "No Thanks");
- if (result) {
- componentUpdater.ReplaceGUIDInTextAssets ();
- } else {
- var assetsToRepair = componentUpdater.GetAssetsToRepair ();
- Debug.LogFormat ("Failed to update the FbxPrefab components in the following files:\n{0}", string.Join ("\n", assetsToRepair));
- }
- }
- else
- {
- UnityEditor.EditorUtility.DisplayDialog(dialogTitle,
- "Couldn't find any prefabs or scenes that require updating", "Ok");
- }
- }
- GUILayout.FlexibleSpace ();
- GUILayout.EndVertical();
- GUILayout.EndScrollView ();
- if (GUI.changed) {
- EditorUtility.SetDirty (exportSettings);
- exportSettings.Save ();
- }
- }
- private static string TryFindDCC(string dccPath, string ext, ExportSettings.DCCType dccType){
- string dccName = "";
- switch (dccType) {
- case ExportSettings.DCCType.Maya:
- dccName = "maya";
- break;
- case ExportSettings.DCCType.Max:
- dccName = "3dsmax";
- break;
- default:
- throw new System.NotImplementedException ();
- }
- if (Path.GetFileNameWithoutExtension (dccPath).ToLower ().Equals (dccName)) {
- return dccPath;
- }
- // clicked on the wrong application, try to see if we can still find
- // a dcc in this directory.
- var dccDir = new DirectoryInfo(Path.GetDirectoryName(dccPath));
- FileSystemInfo[] files = {};
- switch(Application.platform){
- case RuntimePlatform.OSXEditor:
- files = dccDir.GetDirectories ("*." + ext);
- break;
- case RuntimePlatform.WindowsEditor:
- files = dccDir.GetFiles ("*." + ext);
- break;
- default:
- throw new System.NotImplementedException();
- }
- string newDccPath = null;
- foreach (var file in files) {
- var filename = Path.GetFileNameWithoutExtension (file.Name).ToLower ();
- if (filename.Equals (dccName)) {
- newDccPath = file.FullName.Replace("\\","/");
- break;
- }
- }
- return newDccPath;
- }
- [SettingsProvider]
- static SettingsProvider CreateFbxExportSettingsProvider()
- {
- ExportSettings.instance.name = "FBX Export Settings";
- ExportSettings.instance.Load();
- var provider = AssetSettingsProvider.CreateProviderFromObject(
- "Project/Fbx Export", ExportSettings.instance, GetSearchKeywordsFromGUIContentProperties(typeof(Style)));
- #if UNITY_2019_1_OR_NEWER
- provider.inspectorUpdateHandler += () =>
- {
- if (provider.settingsEditor != null &&
- provider.settingsEditor.serializedObject.UpdateIfRequiredOrScript())
- {
- provider.Repaint();
- }
- };
- #else
- provider.activateHandler += (searchContext, rootElement) =>
- {
- if (provider.settingsEditor != null &&
- provider.settingsEditor.serializedObject.UpdateIfRequiredOrScript())
- {
- provider.Repaint();
- }
- };
- #endif // UNITY_2019_1_OR_NEWER
- return provider;
- }
- static IEnumerable<string> GetSearchKeywordsFromGUIContentProperties(Type type)
- {
- return type.GetFields(BindingFlags.Static | BindingFlags.Public)
- .Where(field => typeof(GUIContent).IsAssignableFrom(field.FieldType))
- .Select(field => ((GUIContent)field.GetValue(null)).text)
- .Concat(type.GetProperties(BindingFlags.Static | BindingFlags.Public)
- .Where(prop => typeof(GUIContent).IsAssignableFrom(prop.PropertyType))
- .Select(prop => ((GUIContent)prop.GetValue(null, null)).text))
- .Where(content => content != null)
- .Select(content => content.ToLowerInvariant())
- .Distinct();
- }
- }
- [FilePath("ProjectSettings/FbxExportSettings.asset",FilePathAttribute.Location.ProjectFolder)]
- internal class ExportSettings : ScriptableSingleton<ExportSettings>
- {
- public enum ExportFormat { ASCII = 0, Binary = 1}
- public enum Include { Model = 0, Anim = 1, ModelAndAnim = 2 }
- public enum ObjectPosition { LocalCentered = 0, WorldAbsolute = 1, Reset = 2 /* For convert to model only, no UI option*/}
- public enum LODExportType { All = 0, Highest = 1, Lowest = 2 }
- public bool ExportOutsideProject = false;
- internal const string kDefaultSavePath = ".";
- private static List<string> s_PreferenceList = new List<string>() {kMayaOptionName, kMayaLtOptionName, kMaxOptionName};
- //Any additional names require a space after the name
- internal const string kMaxOptionName = "3ds Max ";
- internal const string kMayaOptionName = "Maya ";
- internal const string kMayaLtOptionName = "Maya LT";
- // NOTE: using "Verbose" and "VerboseProperty" to handle backwards compatibility with older FbxExportSettings.asset files.
- // The variable name is used when serializing, so changing the variable name would prevent older FbxExportSettings.asset files
- // from loading this property.
- [SerializeField]
- private bool Verbose = false;
- internal bool VerboseProperty
- {
- get { return Verbose; }
- set { Verbose = value; }
- }
- private static string DefaultIntegrationSavePath {
- get{
- return Path.GetDirectoryName(Application.dataPath);
- }
- }
- private static string GetMayaLocationFromEnvironmentVariable(string env)
- {
- string result = null;
- if (string.IsNullOrEmpty(env))
- return null;
- string location = Environment.GetEnvironmentVariable(env);
- if (string.IsNullOrEmpty(location))
- return null;
- //Remove any extra slashes on the end
- //Maya would accept a single slash in either direction, so we should be able to
- location = location.Replace("\\", "/");
- location = location.TrimEnd('/');
- if (!Directory.Exists(location))
- return null;
- if (Application.platform == RuntimePlatform.WindowsEditor)
- {
- //If we are on Windows, we need only go up one location to get to the "Autodesk" folder.
- result = Directory.GetParent(location).ToString();
- }
- else if (Application.platform == RuntimePlatform.OSXEditor)
- {
- //We can assume our path is: /Applications/Autodesk/maya2017/Maya.app/Contents
- //So we need to go up three folders.
- var appFolder = Directory.GetParent(location);
- if (appFolder != null)
- {
- var versionFolder = Directory.GetParent(appFolder.ToString());
- if (versionFolder != null)
- {
- var autoDeskFolder = Directory.GetParent(versionFolder.ToString());
- if (autoDeskFolder != null)
- {
- result = autoDeskFolder.ToString();
- }
- }
- }
- }
- return NormalizePath(result, false);
- }
- /// <summary>
- /// Returns a set of valid vendor folder paths with no trailing '/'
- /// </summary>
- [SecurityPermission(SecurityAction.LinkDemand)]
- private static HashSet<string> GetCustomVendorLocations()
- {
- HashSet<string> result = null;
- var environmentVariable = Environment.GetEnvironmentVariable("UNITY_3DAPP_VENDOR_LOCATIONS");
- if (!string.IsNullOrEmpty(environmentVariable))
- {
- result = new HashSet<string>();
- string[] locations = environmentVariable.Split(';');
- foreach (var location in locations)
- {
- if (Directory.Exists(location))
- {
- result.Add(NormalizePath(location, false));
- }
- }
- }
- return result;
- }
- [SecurityPermission(SecurityAction.LinkDemand)]
- private static HashSet<string> GetDefaultVendorLocations()
- {
- if (Application.platform == RuntimePlatform.WindowsEditor)
- {
- HashSet<string> windowsDefaults = new HashSet<string>() { "C:/Program Files/Autodesk" };
- HashSet<string> existingDirectories = new HashSet<string>();
- foreach (string path in windowsDefaults)
- {
- if (Directory.Exists(path))
- {
- existingDirectories.Add(path);
- }
- }
- return existingDirectories;
- }
- else if (Application.platform == RuntimePlatform.OSXEditor)
- {
- HashSet<string> MacOSDefaults = new HashSet<string>() { "/Applications/Autodesk" };
- HashSet<string> existingDirectories = new HashSet<string>();
- foreach (string path in MacOSDefaults)
- {
- if (Directory.Exists(path))
- {
- existingDirectories.Add(path);
- }
- }
- return existingDirectories;
- }
- throw new NotImplementedException();
- }
- /// <summary>
- /// Retrieve available vendor locations.
- /// If there is valid alternative vendor locations, do not use defaults
- /// always use MAYA_LOCATION when available
- /// </summary>
- internal static List<string> DCCVendorLocations
- {
- [SecurityPermission(SecurityAction.LinkDemand)]
- get
- {
- HashSet<string> result = GetCustomVendorLocations();
- if (result == null)
- {
- result = GetDefaultVendorLocations();
- }
- var additionalLocation = GetMayaLocationFromEnvironmentVariable("MAYA_LOCATION");
- if (!string.IsNullOrEmpty(additionalLocation))
- {
- result.Add(additionalLocation);
- }
- return result.ToList<string>();
- }
- }
- [SerializeField]
- private bool launchAfterInstallation = true;
- public bool LaunchAfterInstallation
- {
- get { return launchAfterInstallation; }
- set { launchAfterInstallation = value; }
- }
- [SerializeField]
- private bool HideSendToUnityMenu = true;
- public bool HideSendToUnityMenuProperty
- {
- get { return HideSendToUnityMenu; }
- set { HideSendToUnityMenu = value; }
- }
- [SerializeField]
- private bool BakeAnimation = true;
- internal bool BakeAnimationProperty
- {
- get { return BakeAnimation; }
- set { BakeAnimation = value; }
- }
-
- [SerializeField]
- private bool showConvertToPrefabDialog = true;
- public bool ShowConvertToPrefabDialog
- {
- get { return showConvertToPrefabDialog; }
- set { showConvertToPrefabDialog = value; }
- }
- [SerializeField]
- private string integrationSavePath;
- internal static string IntegrationSavePath
- {
- get
- {
- //If the save path gets messed up and ends up not being valid, just use the project folder as the default
- if (string.IsNullOrEmpty(instance.integrationSavePath) ||
- !Directory.Exists(instance.integrationSavePath))
- {
- //The project folder, above the asset folder
- instance.integrationSavePath = DefaultIntegrationSavePath;
- }
- return instance.integrationSavePath;
- }
- set
- {
- instance.integrationSavePath = value;
- }
- }
- [SerializeField]
- private int selectedDCCApp = 0;
- internal int SelectedDCCApp
- {
- get { return selectedDCCApp; }
- set { selectedDCCApp = value; }
- }
- /// <summary>
- /// The path where Convert To Model will save the new fbx and prefab.
- ///
- /// To help teams work together, this is stored to be relative to the
- /// Application.dataPath, and the path separator is the forward-slash
- /// (e.g. unix and http, not windows).
- ///
- /// Use GetRelativeSavePath / SetRelativeSavePath to get/set this
- /// value, properly interpreted for the current platform.
- /// </summary>
- [SerializeField]
- private List<string> prefabSavePaths = new List<string> ();
- [SerializeField]
- private List<string> fbxSavePaths = new List<string> ();
- [SerializeField]
- private int selectedFbxPath = 0;
- public int SelectedFbxPath
- {
- get { return selectedFbxPath; }
- set { selectedFbxPath = value; }
- }
- [SerializeField]
- private int selectedPrefabPath = 0;
- public int SelectedPrefabPath
- {
- get { return selectedPrefabPath; }
- set { selectedPrefabPath = value; }
- }
- private int maxStoredSavePaths = 5;
- // List of names in order that they appear in option list
- [SerializeField]
- private List<string> dccOptionNames = new List<string>();
- // List of paths in order that they appear in the option list
- [SerializeField]
- private List<string> dccOptionPaths;
- // don't serialize as ScriptableObject does not get properly serialized on export
- [System.NonSerialized]
- private ExportModelSettings m_exportModelSettings;
- internal ExportModelSettings ExportModelSettings
- {
- get { return m_exportModelSettings; }
- set { m_exportModelSettings = value; }
- }
- // store contents of export model settings for serialization
- [SerializeField]
- private ExportModelSettingsSerialize exportModelSettingsSerialize;
-
- [System.NonSerialized]
- private ConvertToPrefabSettings m_convertToPrefabSettings;
- internal ConvertToPrefabSettings ConvertToPrefabSettings
- {
- get { return m_convertToPrefabSettings; }
- set { m_convertToPrefabSettings = value; }
- }
- [SerializeField]
- private ConvertToPrefabSettingsSerialize convertToPrefabSettingsSerialize;
- internal override void LoadDefaults()
- {
- LaunchAfterInstallation = true;
- HideSendToUnityMenuProperty = true;
- prefabSavePaths = new List<string>(){ kDefaultSavePath };
- fbxSavePaths = new List<string> (){ kDefaultSavePath };
- integrationSavePath = DefaultIntegrationSavePath;
- dccOptionPaths = null;
- dccOptionNames = null;
- BakeAnimationProperty = true;
- ExportModelSettings = ScriptableObject.CreateInstance (typeof(ExportModelSettings)) as ExportModelSettings;
- exportModelSettingsSerialize = ExportModelSettings.info;
- ShowConvertToPrefabDialog = true;
- ConvertToPrefabSettings = ScriptableObject.CreateInstance (typeof(ConvertToPrefabSettings)) as ConvertToPrefabSettings;
- convertToPrefabSettingsSerialize = ConvertToPrefabSettings.info;
- }
- /// <summary>
- /// Increments the name if there is a duplicate in dccAppOptions.
- /// </summary>
- /// <returns>The unique name.</returns>
- /// <param name="name">Name.</param>
- internal static string GetUniqueDCCOptionName(string name){
- Debug.Assert(instance != null);
- if (name == null)
- {
- return null;
- }
- if (!instance.dccOptionNames.Contains(name)) {
- return name;
- }
- var format = "{1} ({0})";
- int index = 1;
- // try extracting the current index from the name and incrementing it
- var result = System.Text.RegularExpressions.Regex.Match(name, @"\((?<number>\d+?)\)$");
- if (result != null) {
- var number = result.Groups["number"].Value;
- int tempIndex;
- if (int.TryParse (number, out tempIndex)) {
- var indexOfNumber = name.LastIndexOf (number);
- format = name.Remove (indexOfNumber, number.Length).Insert (indexOfNumber, "{0}");
- index = tempIndex+1;
- }
- }
- string uniqueName = null;
- do {
- uniqueName = string.Format (format, index, name);
- index++;
- } while (instance.dccOptionNames.Contains(uniqueName));
- return uniqueName;
- }
- internal void SetDCCOptionNames(List<string> newList)
- {
- dccOptionNames = newList;
- }
- internal void SetDCCOptionPaths(List<string> newList)
- {
- dccOptionPaths = newList;
- }
- internal void ClearDCCOptionNames()
- {
- dccOptionNames.Clear();
- }
- internal void ClearDCCOptions()
- {
- SetDCCOptionNames(null);
- SetDCCOptionPaths(null);
- }
- /// <summary>
- ///
- /// Find the latest program available and make that the default choice.
- /// Will always take any Maya version over any 3ds Max version.
- ///
- /// Returns the index of the most recent program in the list of dccOptionNames
- /// Returns -1 on error.
- /// </summary>
- internal int PreferredDCCApp
- {
- get
- {
- if (dccOptionNames == null)
- {
- return -1;
- }
- int result = -1;
- int newestDCCVersionNumber = -1;
- for (int i = 0; i < dccOptionNames.Count; i++)
- {
- int versionToCheck = FindDCCVersion(dccOptionNames[i]);
- if (versionToCheck == -1)
- {
- if (dccOptionNames[i] == "MAYA_LOCATION")
- return i;
- continue;
- }
- if (versionToCheck > newestDCCVersionNumber)
- {
- result = i;
- newestDCCVersionNumber = versionToCheck;
- }
- else if (versionToCheck == newestDCCVersionNumber)
- {
- int selection = ChoosePreferredDCCApp(result, i);
- if (selection == i)
- {
- result = i;
- newestDCCVersionNumber = FindDCCVersion(dccOptionNames[i]);
- }
- }
- }
- return result;
- }
- }
- /// <summary>
- /// Takes the index of two program names from dccOptionNames and chooses our preferred one based on the preference list
- /// This happens in case of a tie between two programs with the same release year / version
- /// </summary>
- /// <param name="optionA"></param>
- /// <param name="optionB"></param>
- /// <returns></returns>
- private int ChoosePreferredDCCApp(int optionA, int optionB)
- {
- Debug.Assert(optionA >= 0 && optionB >= 0 && optionA < dccOptionNames.Count && optionB < dccOptionNames.Count);
- if (dccOptionNames.Count == 0)
- {
- return -1;
- }
- var appA = dccOptionNames[optionA];
- var appB = dccOptionNames[optionB];
- if (appA == null || appB == null || appA.Length <= 0 || appB.Length <= 0)
- {
- return -1;
- }
- int scoreA = s_PreferenceList.FindIndex(app => RemoveSpacesAndNumbers(app).Equals(RemoveSpacesAndNumbers(appA)));
- int scoreB = s_PreferenceList.FindIndex(app => RemoveSpacesAndNumbers(app).Equals(RemoveSpacesAndNumbers(appB)));
- return scoreA < scoreB ? optionA : optionB;
- }
- /// <summary>
- /// Takes a given string and removes any spaces or numbers from it
- /// </summary>
- /// <param name="s"></param>
- internal static string RemoveSpacesAndNumbers(string s)
- {
- return System.Text.RegularExpressions.Regex.Replace(s, @"[\s^0-9]", "");
- }
- /// <summary>
- /// Finds the version based off of the title of the application
- /// </summary>
- /// <param name="path"></param>
- /// <returns> the year/version OR -1 if the year could not be parsed </returns>
- private static int FindDCCVersion(string AppName)
- {
- if (string.IsNullOrEmpty(AppName))
- {
- return -1;
- }
- AppName = AppName.Trim();
- if (string.IsNullOrEmpty(AppName))
- return -1;
-
- string[] piecesArray = AppName.Split(' ');
- if (piecesArray.Length < 2)
- {
- return -1;
- }
- //Get the number, which is always the last chunk separated by a space.
- string number = piecesArray[piecesArray.Length - 1];
- int version;
- if (int.TryParse(number, out version))
- {
- return version;
- }
- else
- {
- //remove any letters in the string in a final attempt to extract an int from it (this will happen with MayaLT, for example)
- string AppNameCopy = AppName;
- string stringWithoutLetters = System.Text.RegularExpressions.Regex.Replace(AppNameCopy, "[^0-9]", "");
- if (int.TryParse(stringWithoutLetters, out version))
- {
- return version;
- }
- float fVersion;
- //In case we are looking at something with a decimal based version- the int parse will fail so we'll need to parse it as a float.
- if (float.TryParse(number, out fVersion))
- {
- return (int)fVersion;
- }
- return -1;
- }
- }
- /// <summary>
- /// Find Maya and 3DsMax installations at default install path.
- /// Add results to given dictionary.
- ///
- /// If MAYA_LOCATION is set, add this to the list as well.
- /// </summary>
- [SecurityPermission(SecurityAction.LinkDemand)]
- private static void FindDCCInstalls() {
- var dccOptionNames = instance.dccOptionNames;
- var dccOptionPaths = instance.dccOptionPaths;
- // find dcc installation from vendor locations
- for (int i = 0; i < DCCVendorLocations.Count; i++)
- {
- if (!Directory.Exists(DCCVendorLocations[i]))
- {
- // no autodesk products installed
- continue;
- }
- // List that directory and find the right version:
- // either the newest version, or the exact version we wanted.
- var adskRoot = new System.IO.DirectoryInfo(DCCVendorLocations[i]);
- foreach (var productDir in adskRoot.GetDirectories())
- {
- var product = productDir.Name;
- // Only accept those that start with 'maya' in either case.
- if (product.StartsWith ("maya", StringComparison.InvariantCultureIgnoreCase)) {
- string version = product.Substring ("maya".Length);
- dccOptionPaths.Add (GetMayaExePathFromLocation (productDir.FullName.Replace ("\\", "/")));
- dccOptionNames.Add (GetUniqueDCCOptionName(kMayaOptionName + version));
- continue;
- }
- if (product.StartsWith("3ds max", StringComparison.InvariantCultureIgnoreCase))
- {
- var exePath = string.Format("{0}/{1}", productDir.FullName.Replace("\\", "/"), "3dsmax.exe");
- string version = product.Substring("3ds max ".Length);
- var maxOptionName = GetUniqueDCCOptionName(kMaxOptionName + version);
- if (IsEarlierThanMax2017(maxOptionName))
- {
- continue;
- }
- dccOptionPaths.Add(exePath);
- dccOptionNames.Add(maxOptionName);
- }
- }
- }
- // add extra locations defined by special environment variables
- string location = GetMayaLocationFromEnvironmentVariable("MAYA_LOCATION");
- if (!string.IsNullOrEmpty(location))
- {
- dccOptionPaths.Add(GetMayaExePathFromLocation(location));
- dccOptionNames.Add("MAYA_LOCATION");
- }
- instance.SelectedDCCApp = instance.PreferredDCCApp;
- }
- /// <summary>
- /// Returns the first valid folder in our list of vendor locations
- /// </summary>
- /// <returns>The first valid vendor location</returns>
- internal static string FirstValidVendorLocation
- {
- [SecurityPermission(SecurityAction.LinkDemand)]
- get
- {
- List<string> locations = DCCVendorLocations;
- for (int i = 0; i < locations.Count; i++)
- {
- //Look through the list of locations we have and take the first valid one
- if (Directory.Exists(locations[i]))
- {
- return locations[i];
- }
- }
- //if no valid locations exist, just take us to the project folder
- return Directory.GetCurrentDirectory();
- }
- }
- /// <summary>
- /// Gets the maya exe at Maya install location.
- /// </summary>
- /// <returns>The maya exe path.</returns>
- /// <param name="location">Location of Maya install.</param>
- private static string GetMayaExePathFromLocation(string location)
- {
- switch (Application.platform) {
- case RuntimePlatform.WindowsEditor:
- return location + "/bin/maya.exe";
- case RuntimePlatform.OSXEditor:
- // MAYA_LOCATION on mac is set by Autodesk to be the
- // Contents directory. But let's make it easier on people
- // and allow just having it be the app bundle or a
- // directory that holds the app bundle.
- if (location.EndsWith(".app/Contents")) {
- return location + "/MacOS/Maya";
- } else if (location.EndsWith(".app")) {
- return location + "/Contents/MacOS/Maya";
- } else {
- return location + "/Maya.app/Contents/MacOS/Maya";
- }
- default:
- throw new NotImplementedException ();
- }
- }
- [SecurityPermission(SecurityAction.LinkDemand)]
- internal static GUIContent[] GetDCCOptions(){
- if (instance.dccOptionNames == null ||
- instance.dccOptionNames.Count != instance.dccOptionPaths.Count ||
- instance.dccOptionNames.Count == 0) {
- instance.dccOptionPaths = new List<string> ();
- instance.dccOptionNames = new List<string> ();
- FindDCCInstalls ();
- }
- // store the selected app if any
- string prevSelection = SelectedDCCPath;
- // remove options that no longer exist
- List<string> pathsToDelete = new List<string>();
- List<string> namesToDelete = new List<string>();
- for(int i = 0; i < instance.dccOptionPaths.Count; i++) {
- var dccPath = instance.dccOptionPaths [i];
- if (!File.Exists (dccPath)) {
- namesToDelete.Add (instance.dccOptionNames [i]);
- pathsToDelete.Add (dccPath);
- }
- }
- foreach (var str in pathsToDelete) {
- instance.dccOptionPaths.Remove (str);
- }
- foreach (var str in namesToDelete) {
- instance.dccOptionNames.Remove (str);
- }
- // set the selected DCC app to the previous selection
- instance.SelectedDCCApp = instance.dccOptionPaths.IndexOf (prevSelection);
- if (instance.SelectedDCCApp < 0) {
- // find preferred app if previous selection no longer exists
- instance.SelectedDCCApp = instance.PreferredDCCApp;
- }
- if (instance.dccOptionPaths.Count <= 0) {
- instance.SelectedDCCApp = 0;
- return new GUIContent[]{
- new GUIContent("<No 3D Application found>")
- };
- }
- GUIContent[] optionArray = new GUIContent[instance.dccOptionPaths.Count];
- for(int i = 0; i < instance.dccOptionPaths.Count; i++){
- optionArray [i] = new GUIContent(
- instance.dccOptionNames[i],
- instance.dccOptionPaths[i]
- );
- }
- return optionArray;
- }
- internal enum DCCType { Maya, Max };
- [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
- [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
- internal static void AddDCCOption(string newOption, DCCType dcc){
- if (Application.platform == RuntimePlatform.OSXEditor && dcc == DCCType.Maya) {
- // on OSX we get a path ending in .app, which is not quite the exe
- newOption = GetMayaExePathFromLocation(newOption);
- }
- var dccOptionPaths = instance.dccOptionPaths;
- if (dccOptionPaths.Contains(newOption)) {
- instance.SelectedDCCApp = dccOptionPaths.IndexOf (newOption);
- return;
- }
- string optionName = "";
- switch (dcc) {
- case DCCType.Maya:
- var version = AskMayaVersion(newOption);
- if (version == null)
- {
- Debug.LogError("This version of Maya could not be launched properly");
- UnityEditor.EditorUtility.DisplayDialog("Error Loading 3D Application",
- "Failed to add Maya option, could not get version number from maya.exe",
- "Ok");
- return;
- }
- optionName = GetUniqueDCCOptionName("Maya " + version);
- break;
- case DCCType.Max:
- optionName = GetMaxOptionName (newOption);
- if (ExportSettings.IsEarlierThanMax2017(optionName))
- {
- Debug.LogError("Earlier than 3ds Max 2017 is not supported");
- UnityEditor.EditorUtility.DisplayDialog(
- "Error adding 3D Application",
- "Unity Integration only supports 3ds Max 2017 or later",
- "Ok");
- return;
- }
- break;
- default:
- throw new System.NotImplementedException();
- }
- instance.dccOptionNames.Add (optionName);
- dccOptionPaths.Add (newOption);
- instance.SelectedDCCApp = dccOptionPaths.Count - 1;
- }
- /// <summary>
- /// Ask the version number by running maya.
- /// </summary>
- [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
- [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
- internal static string AskMayaVersion(string exePath) {
- System.Diagnostics.Process myProcess = new System.Diagnostics.Process();
- myProcess.StartInfo.FileName = exePath;
- myProcess.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
- myProcess.StartInfo.CreateNoWindow = true;
- myProcess.StartInfo.UseShellExecute = false;
- myProcess.StartInfo.RedirectStandardOutput = true;
- myProcess.StartInfo.Arguments = "-v";
- myProcess.EnableRaisingEvents = true;
- myProcess.Start();
- string resultString = myProcess.StandardOutput.ReadToEnd();
- myProcess.WaitForExit();
- // Output is like: Maya 2018, Cut Number 201706261615
- // We want the stuff after 'Maya ' and before the comma.
- // (Uni-31601) less brittle! Consider also the mel command "about -version".
- if (string.IsNullOrEmpty(resultString))
- {
- return null;
- }
- resultString = resultString.Trim();
- var commaIndex = resultString.IndexOf(',');
- if (commaIndex != -1)
- {
- const int versionStart = 5; // length of "Maya "
- return resultString.Length > versionStart ? resultString.Substring(0, commaIndex).Substring(versionStart) : null;
- }
- else
- {
- //This probably means we tried to launch Maya to check the version but it was some sort of broken maya.
- //We'll just return null and throw an error for it.
- return null;
- }
- }
- /// <summary>
- /// Gets the unique label for a new 3DsMax dropdown option.
- /// </summary>
- /// <returns>The 3DsMax dropdown option label.</returns>
- /// <param name="exePath">Exe path.</param>
- internal static string GetMaxOptionName(string exePath){
- return GetUniqueDCCOptionName(Path.GetFileName(Path.GetDirectoryName (exePath)));
- }
- internal static bool IsEarlierThanMax2017(string AppName){
- int version = FindDCCVersion(AppName);
- return version != -1 && version < 2017;
- }
- internal static string SelectedDCCPath
- {
- get
- {
- return (instance.dccOptionPaths.Count > 0 &&
- instance.SelectedDCCApp >= 0 &&
- instance.SelectedDCCApp < instance.dccOptionPaths.Count) ? instance.dccOptionPaths[instance.SelectedDCCApp] : "";
- }
- }
- internal static string SelectedDCCName
- {
- get
- {
- return (instance.dccOptionNames.Count > 0 &&
- instance.SelectedDCCApp >= 0 &&
- instance.SelectedDCCApp < instance.dccOptionNames.Count) ? instance.dccOptionNames[instance.SelectedDCCApp] : "";
- }
- }
- internal static bool CanInstall()
- {
- return instance.dccOptionPaths.Count > 0;
- }
- internal static string GetProjectRelativePath(string fullPath){
- var assetRelativePath = UnityEditor.Formats.Fbx.Exporter.ExportSettings.ConvertToAssetRelativePath(fullPath);
- var projectRelativePath = "Assets/" + assetRelativePath;
- if (string.IsNullOrEmpty(assetRelativePath)) {
- throw new FbxExportSettingsException("Path " + fullPath + " must be in the Assets folder.");
- }
- return projectRelativePath;
- }
- /// <summary>
- /// The relative save paths for given absolute paths.
- /// This is relative to the Application.dataPath ; it uses '/' as the
- /// separator on all platforms.
- /// </summary>
- internal static string[] GetRelativeSavePaths(List<string> exportSavePaths){
- if(exportSavePaths == null)
- {
- return null;
- }
- if (exportSavePaths.Count == 0) {
- exportSavePaths.Add (kDefaultSavePath);
- }
- string[] relSavePaths = new string[exportSavePaths.Count];
- // use special forward slash unicode char as "/" is a special character
- // that affects the dropdown layout.
- string forwardslash = " \u2044 ";
- for (int i = 0; i < relSavePaths.Length; i++) {
- relSavePaths [i] = string.Format("Assets{0}{1}", forwardslash, exportSavePaths[i] == "."? "" : NormalizePath(exportSavePaths [i], isRelative: true).Replace("/", forwardslash));
- }
- return relSavePaths;
- }
- /// <summary>
- /// Returns the paths for display in the menu.
- /// Paths inside the Assets folder are relative, while those outside are kept absolute.
- /// </summary>
- internal static string[] GetMixedSavePaths(List<string> exportSavePaths)
- {
- string[] displayPaths = new string[exportSavePaths.Count];
- string forwardslash = " \u2044 ";
- for (int i = 0; i < displayPaths.Length; i++)
- {
- // if path is in Assets folder, shorten it
- if (!Path.IsPathRooted(exportSavePaths[i]))
- {
- displayPaths[i] = string.Format("Assets{0}{1}", forwardslash, exportSavePaths[i] == "."? "" : NormalizePath(exportSavePaths [i], isRelative: true).Replace("/", forwardslash));
- }
- else
- {
- displayPaths[i] = exportSavePaths[i].Replace("/", forwardslash);
- }
- }
- return displayPaths;
- }
- /// <summary>
- /// The path where Export model will save the new fbx.
- /// This is relative to the Application.dataPath ; it uses '/' as the
- /// separator on all platforms.
- /// Only returns the paths within the Assets folder of the project.
- /// </summary>
- internal static string[] GetRelativeFbxSavePaths(){
- // sort the list of paths, putting project paths first
- instance.fbxSavePaths.Sort((x, y) => Path.IsPathRooted(x).CompareTo(Path.IsPathRooted(y)));
- var relPathCount = instance.fbxSavePaths.FindAll(x => !Path.IsPathRooted(x)).Count;
- // reset selected path if it's out of range
- if (instance.SelectedFbxPath > relPathCount - 1)
- {
- instance.SelectedFbxPath = 0;
- }
- return GetRelativeSavePaths(instance.fbxSavePaths.GetRange(0, relPathCount));
- }
- /// <summary>
- /// The path where Convert to Prefab will save the new prefab.
- /// This is relative to the Application.dataPath ; it uses '/' as the
- /// separator on all platforms.
- /// </summary>
- internal static string[] GetRelativePrefabSavePaths(){
- return GetRelativeSavePaths(instance.prefabSavePaths);
- }
- /// <summary>
- /// The paths formatted for display in the menu.
- /// Paths outside the Assets folder are kept as they are and ones inside are shortened.
- /// </summary>
- internal static string[] GetMixedFbxSavePaths()
- {
- return GetMixedSavePaths(instance.fbxSavePaths);
- }
- /// <summary>
- /// Adds the save path to given save path list.
- /// </summary>
- /// <param name="savePath">Save path.</param>
- /// <param name="exportSavePaths">Export save paths.</param>
- private static void AddSavePath(string savePath, ref List<string> exportSavePaths){
- if(exportSavePaths == null)
- {
- return;
- }
- if (ExportSettings.instance.ExportOutsideProject)
- {
- savePath = NormalizePath(savePath, isRelative: false);
- }
- else
- {
- savePath = NormalizePath(savePath, isRelative: true);
- }
- if (exportSavePaths.Contains (savePath)) {
- // move to first place if it isn't already
- if (exportSavePaths [0] == savePath) {
- return;
- }
- exportSavePaths.Remove (savePath);
- }
- if (exportSavePaths.Count >= instance.maxStoredSavePaths) {
- // remove last used path
- exportSavePaths.RemoveAt(exportSavePaths.Count-1);
- }
- exportSavePaths.Insert (0, savePath);
- }
- internal static void AddFbxSavePath(string savePath){
- AddSavePath (savePath, ref instance.fbxSavePaths);
- instance.SelectedFbxPath = 0;
- }
- internal static void AddPrefabSavePath(string savePath){
- AddSavePath (savePath, ref instance.prefabSavePaths);
- instance.SelectedPrefabPath = 0;
- }
- internal static string GetAbsoluteSavePath(string savePath){
- var projectAbsolutePath = Path.Combine(Application.dataPath, savePath);
- projectAbsolutePath = NormalizePath(projectAbsolutePath, isRelative: false, separator: Path.DirectorySeparatorChar);
-
- // if path is outside Assets folder, it's already absolute so return the original path
- if (string.IsNullOrEmpty(ExportSettings.ConvertToAssetRelativePath(projectAbsolutePath)))
- {
- return savePath;
- }
- return projectAbsolutePath;
- }
- internal static string FbxAbsoluteSavePath{
- get
- {
- if (instance.fbxSavePaths.Count <= 0)
- {
- instance.fbxSavePaths.Add(kDefaultSavePath);
- }
- return GetAbsoluteSavePath(instance.fbxSavePaths[instance.SelectedFbxPath]);
- }
- }
- internal static string PrefabAbsoluteSavePath{
- get
- {
- if (instance.prefabSavePaths.Count <= 0)
- {
- instance.prefabSavePaths.Add(kDefaultSavePath);
- }
- return GetAbsoluteSavePath(instance.prefabSavePaths[instance.SelectedPrefabPath]);
- }
- }
- /// <summary>
- /// Convert an absolute path into a relative path like what you would
- /// get from GetRelativeSavePath.
- ///
- /// This uses '/' as the path separator.
- ///
- /// If 'requireSubdirectory' is the default on, return empty-string if the full
- /// path is not in a subdirectory of assets.
- /// </summary>
- internal static string ConvertToAssetRelativePath(string fullPathInAssets, bool requireSubdirectory = true)
- {
- if (!Path.IsPathRooted(fullPathInAssets)) {
- fullPathInAssets = Path.GetFullPath(fullPathInAssets);
- }
- var relativePath = GetRelativePath(Application.dataPath, fullPathInAssets);
- if (requireSubdirectory && relativePath.StartsWith("..")) {
- if (relativePath.Length == 2 || relativePath[2] == '/') {
- // The relative path has us pop out to another directory,
- // so return an empty string as requested.
- return "";
- }
- }
- return relativePath;
- }
- /// <summary>
- /// Compute how to get from 'fromDir' to 'toDir' via a relative path.
- /// </summary>
- internal static string GetRelativePath(string fromDir, string toDir,
- char separator = '/')
- {
- // https://stackoverflow.com/questions/275689/how-to-get-relative-path-from-absolute-path
- // Except... the MakeRelativeUri that ships with Unity is buggy.
- // e.g. https://bugzilla.xamarin.com/show_bug.cgi?id=5921
- // among other bugs. So we roll our own.
- // Normalize the paths, assuming they're absolute paths (if they
- // aren't, they get normalized as relative paths)
- fromDir = NormalizePath(fromDir, isRelative: false);
- toDir = NormalizePath(toDir, isRelative: false);
- // Break them into path components.
- var fromDirs = fromDir.Split('/');
- var toDirs = toDir.Split('/');
- // Find the least common ancestor
- int lca = -1;
- for(int i = 0, n = System.Math.Min(fromDirs.Length, toDirs.Length); i < n; ++i) {
- if (fromDirs[i] != toDirs[i]) { break; }
- lca = i;
- }
- // Step up from the fromDir to the lca, then down from lca to the toDir.
- // If from = /a/b/c/d
- // and to = /a/b/e/f/g
- // Then we need to go up 2 and down 3.
- var nStepsUp = (fromDirs.Length - 1) - lca;
- var nStepsDown = (toDirs.Length - 1) - lca;
- if (nStepsUp + nStepsDown == 0) {
- return ".";
- }
- var relDirs = new string[nStepsUp + nStepsDown];
- for(int i = 0; i < nStepsUp; ++i) {
- relDirs[i] = "..";
- }
- for(int i = 0; i < nStepsDown; ++i) {
- relDirs[nStepsUp + i] = toDirs[lca + 1 + i];
- }
- return string.Join("" + separator, relDirs);
- }
- /// <summary>
- /// Normalize a path, cleaning up path separators, resolving '.' and
- /// '..', removing duplicate and trailing path separators, etc.
- ///
- /// If the path passed in is a relative path, we remove leading path separators.
- /// If it's an absolute path we don't.
- ///
- /// If you claim the path is absolute but actually it's relative, we
- /// treat it as a relative path.
- /// </summary>
- internal static string NormalizePath(string path, bool isRelative,
- char separator = '/')
- {
- if(path == null)
- {
- return null;
- }
- // Use slashes to simplify the code (we're going to clobber them all anyway).
- path = path.Replace('\\', '/');
- // If we're supposed to be an absolute path, but we're actually a
- // relative path, ignore the 'isRelative' flag.
- if (!isRelative && !Path.IsPathRooted(path)) {
- isRelative = true;
- }
- // Build up a list of directory items.
- var dirs = path.Split('/');
- // Modify dirs in-place, reading from readIndex and remembering
- // what index we've written to.
- int lastWriteIndex = -1;
- for (int readIndex = 0, n = dirs.Length; readIndex < n; ++readIndex) {
- var dir = dirs[readIndex];
- // Skip duplicate path separators.
- if (string.IsNullOrEmpty(dir)) {
- // Skip if it's not a leading path separator.
- if (lastWriteIndex >= 0) {
- continue; }
- // Also skip if it's leading and we have a relative path.
- if (isRelative) {
- continue;
- }
- }
- // Skip '.'
- if (dir == ".") {
- continue;
- }
- // Erase the previous directory we read on '..'.
- // Exception: we can start with '..'
- // Exception: we can have multiple '..' in a row.
- //
- // Note: this ignores the actual file system and the funny
- // results you see when there are symlinks.
- if (dir == "..") {
- if (lastWriteIndex == -1) {
- // Leading '..' => handle like a normal directory.
- } else if (dirs[lastWriteIndex] == "..") {
- // Multiple ".." => handle like a normal directory.
- } else {
- // Usual case: delete the previous directory.
- lastWriteIndex--;
- continue;
- }
- }
- // Copy anything else to the next index.
- ++lastWriteIndex;
- dirs[lastWriteIndex] = dirs[readIndex];
- }
- if (lastWriteIndex == -1 || (lastWriteIndex == 0 && string.IsNullOrEmpty(dirs[lastWriteIndex]))) {
- // If we didn't keep anything, we have the empty path.
- // For an absolute path that's / ; for a relative path it's .
- if (isRelative) {
- return ".";
- } else {
- return "" + separator;
- }
- } else {
- // Otherwise print out the path with the proper separator.
- return String.Join("" + separator, dirs, 0, lastWriteIndex + 1);
- }
- }
- internal override void Load ()
- {
- base.Load ();
- if (!instance.ExportModelSettings) {
- instance.ExportModelSettings = ScriptableObject.CreateInstance (typeof(ExportModelSettings)) as ExportModelSettings;
- }
- instance.ExportModelSettings.info = instance.exportModelSettingsSerialize;
-
- if (!instance.ConvertToPrefabSettings) {
- instance.ConvertToPrefabSettings = ScriptableObject.CreateInstance (typeof(ConvertToPrefabSettings)) as ConvertToPrefabSettings;
- }
- instance.ConvertToPrefabSettings.info = instance.convertToPrefabSettingsSerialize;
- }
- internal void Save()
- {
- exportModelSettingsSerialize = ExportModelSettings.info;
- convertToPrefabSettingsSerialize = ConvertToPrefabSettings.info;
- instance.Save (true);
- }
- }
- internal abstract class ScriptableSingleton<T> : ScriptableObject where T : ScriptableSingleton<T>
- {
- private static T s_Instance;
- public static T instance
- {
- get
- {
- if (s_Instance == null)
- {
- s_Instance = ScriptableObject.CreateInstance<T>();
- s_Instance.Load();
- }
- return s_Instance;
- }
- }
- internal ScriptableSingleton()
- {
- if (s_Instance != null)
- {
- Debug.LogError(typeof(T) + " already exists. Did you query the singleton in a constructor?");
- }
- }
- internal abstract void LoadDefaults();
- internal virtual void Load()
- {
- string filePath = GetFilePath();
- if (!System.IO.File.Exists(filePath)) {
- LoadDefaults();
- } else {
- try {
- var fileData = System.IO.File.ReadAllText(filePath);
- EditorJsonUtility.FromJsonOverwrite(fileData, s_Instance);
- } catch(Exception xcp) {
- // Quash the exception and take the default settings.
- Debug.LogException(xcp);
- LoadDefaults();
- }
- }
- }
- internal virtual void Save(bool saveAsText)
- {
- if (s_Instance == null)
- {
- Debug.Log("Cannot save ScriptableSingleton: no instance!");
- return;
- }
- string filePath = GetFilePath();
- if (!string.IsNullOrEmpty(filePath))
- {
- string directoryName = Path.GetDirectoryName(filePath);
- if (!Directory.Exists(directoryName))
- {
- Directory.CreateDirectory(directoryName);
- }
- System.IO.File.WriteAllText(filePath, EditorJsonUtility.ToJson(s_Instance, true));
- }
- }
- private static string GetFilePath()
- {
- foreach(var attr in typeof(T).GetCustomAttributes(true)) {
- FilePathAttribute filePathAttribute = attr as FilePathAttribute;
- if (filePathAttribute != null)
- {
- return filePathAttribute.filepath;
- }
- }
- return null;
- }
- }
- [AttributeUsage(AttributeTargets.Class)]
- internal sealed class FilePathAttribute : Attribute
- {
- public enum Location
- {
- PreferencesFolder,
- ProjectFolder
- }
- public string filepath
- {
- get;
- set;
- }
- public FilePathAttribute(string relativePath, FilePathAttribute.Location location)
- {
- if (string.IsNullOrEmpty(relativePath))
- {
- Debug.LogError("Invalid relative path! (its null or empty)");
- return;
- }
- if (relativePath[0] == '/')
- {
- relativePath = relativePath.Substring(1);
- }
- if (location == FilePathAttribute.Location.PreferencesFolder)
- {
- this.filepath = InternalEditorUtility.unityPreferencesFolder + "/" + relativePath;
- }
- else
- {
- this.filepath = relativePath;
- }
- }
- }
- }
|