3
3
using UnityEditorInternal ;
4
4
using UnityEngine ;
5
5
using UnityEditor ;
6
+ using System . Collections . Generic ;
6
7
7
8
namespace FbxExporters . EditorTools {
8
9
@@ -98,8 +99,63 @@ public override void OnInspectorGUI() {
98
99
GUILayout . EndHorizontal ( ) ;
99
100
100
101
EditorGUILayout . Space ( ) ;
101
- var installIntegrationContent = new GUIContent (
102
- "Install Maya Integration" ,
102
+
103
+ GUILayout . BeginHorizontal ( ) ;
104
+ GUILayout . Label ( new GUIContent (
105
+ "Maya Application:" ,
106
+ "Select the version of Maya where you would like to install the Unity integration." ) ) ;
107
+
108
+ // dropdown to select Maya version to use
109
+ var options = ExportSettings . GetMayaOptions ( ) ;
110
+ // make sure we never initially have browse selected
111
+ if ( exportSettings . selectedMayaApp == options . Length - 1 ) {
112
+ exportSettings . selectedMayaApp = 0 ;
113
+ }
114
+ int oldValue = exportSettings . selectedMayaApp ;
115
+ exportSettings . selectedMayaApp = EditorGUILayout . Popup ( exportSettings . selectedMayaApp , options ) ;
116
+ if ( exportSettings . selectedMayaApp == options . Length - 1 ) {
117
+ var ext = "exe" ;
118
+ #if UNITY_EDITOR_OSX
119
+ ext = "app" ;
120
+ #endif
121
+ string mayaPath = EditorUtility . OpenFilePanel ( "Select Maya Application" , ExportSettings . kDefaultAdskRoot , ext ) ;
122
+
123
+ // check that the path is valid and references the maya executable
124
+ if ( ! string . IsNullOrEmpty ( mayaPath ) ) {
125
+ if ( ! Path . GetFileNameWithoutExtension ( mayaPath ) . ToLower ( ) . Equals ( "maya" ) ) {
126
+ // clicked on the wrong application, try to see if we can still find
127
+ // maya in this directory.
128
+ var mayaDir = new DirectoryInfo ( Path . GetDirectoryName ( mayaPath ) ) ;
129
+ #if UNITY_EDITOR_OSX
130
+ var files = mayaDir . GetDirectories ( "*." + ext ) ;
131
+ #else
132
+ var files = mayaDir . GetFiles ( "*." + ext ) ;
133
+ #endif
134
+ bool foundMaya = false ;
135
+ foreach ( var file in files ) {
136
+ var filename = Path . GetFileNameWithoutExtension ( file . Name ) . ToLower ( ) ;
137
+ if ( filename . Equals ( "maya" ) ) {
138
+ mayaPath = file . FullName . Replace ( "\\ " , "/" ) ;
139
+ foundMaya = true ;
140
+ break ;
141
+ }
142
+ }
143
+ if ( ! foundMaya ) {
144
+ Debug . LogError ( string . Format ( "Could not find Maya at: \" {0}\" " , mayaDir . FullName ) ) ;
145
+ exportSettings . selectedMayaApp = oldValue ;
146
+ return ;
147
+ }
148
+ }
149
+ ExportSettings . AddMayaOption ( mayaPath ) ;
150
+ Repaint ( ) ;
151
+ } else {
152
+ exportSettings . selectedMayaApp = oldValue ;
153
+ }
154
+ }
155
+ GUILayout . EndHorizontal ( ) ;
156
+
157
+ var installIntegrationContent = new GUIContent (
158
+ "Install Unity Integration" ,
103
159
"Install and configure the Unity integration for Maya so that you can import and export directly to this project." ) ;
104
160
if ( GUILayout . Button ( installIntegrationContent ) ) {
105
161
FbxExporters . Editor . IntegrationsUI . InstallMayaIntegration ( ) ;
@@ -121,6 +177,20 @@ public class ExportSettings : ScriptableSingleton<ExportSettings>
121
177
{
122
178
public const string kDefaultSavePath = "." ;
123
179
180
+ /// <summary>
181
+ /// The path where all the different versions of Maya are installed
182
+ /// by default. Depends on the platform.
183
+ /// </summary>
184
+ public const string kDefaultAdskRoot =
185
+ #if UNITY_EDITOR_OSX
186
+ "/Applications/Autodesk"
187
+ #elif UNITY_EDITOR_LINUX
188
+ "/usr/autodesk"
189
+ #else // WINDOWS
190
+ "C:/Program Files/Autodesk"
191
+ #endif
192
+ ;
193
+
124
194
// Note: default values are set in LoadDefaults().
125
195
public bool mayaCompatibleNames ;
126
196
public bool centerObjects ;
@@ -132,6 +202,8 @@ public class ExportSettings : ScriptableSingleton<ExportSettings>
132
202
[ HideInInspector ]
133
203
public bool keepOriginalAfterConvert ;
134
204
205
+ public int selectedMayaApp = 0 ;
206
+
135
207
[ SerializeField ]
136
208
public UnityEngine . Object turntableScene ;
137
209
@@ -148,13 +220,201 @@ public class ExportSettings : ScriptableSingleton<ExportSettings>
148
220
[ SerializeField ]
149
221
string convertToModelSavePath ;
150
222
223
+ // List of names in order that they appear in option list
224
+ [ SerializeField ]
225
+ private List < string > mayaOptionNames ;
226
+ // List of paths in order that they appear in the option list
227
+ [ SerializeField ]
228
+ private List < string > mayaOptionPaths ;
229
+
151
230
protected override void LoadDefaults ( )
152
231
{
153
232
mayaCompatibleNames = true ;
154
233
centerObjects = true ;
155
234
keepOriginalAfterConvert = false ;
156
235
convertToModelSavePath = kDefaultSavePath ;
157
236
turntableScene = null ;
237
+ mayaOptionPaths = null ;
238
+ mayaOptionNames = null ;
239
+ }
240
+
241
+ /// <summary>
242
+ /// Increments the name if there is a duplicate in MayaAppOptions dictionary.
243
+ /// </summary>
244
+ /// <returns>The unique name.</returns>
245
+ /// <param name="name">Name.</param>
246
+ private static string GetUniqueName ( string name ) {
247
+ if ( ! instance . mayaOptionNames . Contains ( name ) ) {
248
+ return name ;
249
+ }
250
+ var format = "{1} ({0})" ;
251
+ int index = 1 ;
252
+ // try extracting the current index from the name and incrementing it
253
+ var result = System . Text . RegularExpressions . Regex . Match ( name , @"\((?<number>\d+?)\)$" ) ;
254
+ if ( result != null ) {
255
+ var number = result . Groups [ "number" ] . Value ;
256
+ int tempIndex ;
257
+ if ( int . TryParse ( number , out tempIndex ) ) {
258
+ var indexOfNumber = name . LastIndexOf ( number ) ;
259
+ format = name . Remove ( indexOfNumber , number . Length ) . Insert ( indexOfNumber , "{0}" ) ;
260
+ index = tempIndex + 1 ;
261
+ }
262
+ }
263
+
264
+ string uniqueName = null ;
265
+ do {
266
+ uniqueName = string . Format ( format , index , name ) ;
267
+ index ++ ;
268
+ } while ( instance . mayaOptionNames . Contains ( name ) ) ;
269
+
270
+ return uniqueName ;
271
+ }
272
+
273
+ /// <summary>
274
+ /// Find Maya installations at default install path.
275
+ /// Add results to given dictionary.
276
+ ///
277
+ /// If MAYA_LOCATION is set, add this to the list as well.
278
+ /// </summary>
279
+ private static void FindMayaInstalls ( ) {
280
+ instance . mayaOptionPaths = new List < string > ( ) ;
281
+ instance . mayaOptionNames = new List < string > ( ) ;
282
+ var mayaOptionName = instance . mayaOptionNames ;
283
+ var mayaOptionPath = instance . mayaOptionPaths ;
284
+
285
+ // If the location is given by the environment, use it.
286
+ var location = System . Environment . GetEnvironmentVariable ( "MAYA_LOCATION" ) ;
287
+ if ( ! string . IsNullOrEmpty ( location ) ) {
288
+ location = location . TrimEnd ( '/' ) ;
289
+ mayaOptionPath . Add ( GetMayaExePath ( location . Replace ( "\\ " , "/" ) ) ) ;
290
+ mayaOptionName . Add ( "MAYA_LOCATION" ) ;
291
+ }
292
+
293
+ // List that directory and find the right version:
294
+ // either the newest version, or the exact version we wanted.
295
+ var adskRoot = new System . IO . DirectoryInfo ( kDefaultAdskRoot ) ;
296
+ foreach ( var productDir in adskRoot . GetDirectories ( ) ) {
297
+ var product = productDir . Name ;
298
+
299
+ // Only accept those that start with 'maya' in either case.
300
+ if ( ! product . StartsWith ( "maya" , StringComparison . InvariantCultureIgnoreCase ) ) {
301
+ continue ;
302
+ }
303
+ // Reject MayaLT -- it doesn't have plugins.
304
+ if ( product . StartsWith ( "mayalt" , StringComparison . InvariantCultureIgnoreCase ) ) {
305
+ continue ;
306
+ }
307
+ string version = product . Substring ( "maya" . Length ) ;
308
+ mayaOptionPath . Add ( GetMayaExePath ( productDir . FullName . Replace ( "\\ " , "/" ) ) ) ;
309
+ mayaOptionName . Add ( GetUniqueName ( "Maya " + version ) ) ;
310
+ }
311
+ }
312
+
313
+ /// <summary>
314
+ /// Gets the maya exe at Maya install location.
315
+ /// </summary>
316
+ /// <returns>The maya exe path.</returns>
317
+ /// <param name="location">Location of Maya install.</param>
318
+ private static string GetMayaExePath ( string location )
319
+ {
320
+ #if UNITY_EDITOR_OSX
321
+ // MAYA_LOCATION on mac is set by Autodesk to be the
322
+ // Contents directory. But let's make it easier on people
323
+ // and allow just having it be the app bundle or a
324
+ // directory that holds the app bundle.
325
+ if ( location . EndsWith ( ".app/Contents" ) ) {
326
+ return location + "/MacOS/Maya" ;
327
+ } else if ( location . EndsWith ( ".app" ) ) {
328
+ return location + "/Contents/MacOS/Maya" ;
329
+ } else {
330
+ return location + "/Maya.app/Contents/MacOS/Maya" ;
331
+ }
332
+ #elif UNITY_EDITOR_LINUX
333
+ return location + "/bin/maya" ;
334
+ #else // WINDOWS
335
+ return location + "/bin/maya.exe" ;
336
+ #endif
337
+ }
338
+
339
+ public static GUIContent [ ] GetMayaOptions ( ) {
340
+ if ( instance . mayaOptionNames == null ||
341
+ instance . mayaOptionNames . Count != instance . mayaOptionPaths . Count ||
342
+ instance . mayaOptionNames . Count == 0 ) {
343
+ FindMayaInstalls ( ) ;
344
+ }
345
+
346
+ // remove options that no longer exist
347
+ List < int > toDelete = new List < int > ( ) ;
348
+ for ( int i = 0 ; i < instance . mayaOptionPaths . Count ; i ++ ) {
349
+ var mayaPath = instance . mayaOptionPaths [ i ] ;
350
+ if ( ! File . Exists ( mayaPath ) ) {
351
+ if ( i == instance . selectedMayaApp ) {
352
+ instance . selectedMayaApp = 0 ;
353
+ }
354
+ instance . mayaOptionNames . RemoveAt ( i ) ;
355
+ toDelete . Add ( i ) ;
356
+ }
357
+ }
358
+ foreach ( var index in toDelete ) {
359
+ instance . mayaOptionPaths . RemoveAt ( index ) ;
360
+ }
361
+
362
+ GUIContent [ ] optionArray = new GUIContent [ instance . mayaOptionPaths . Count + 1 ] ;
363
+ for ( int i = 0 ; i < instance . mayaOptionPaths . Count ; i ++ ) {
364
+ optionArray [ i ] = new GUIContent (
365
+ instance . mayaOptionNames [ i ] ,
366
+ instance . mayaOptionPaths [ i ]
367
+ ) ;
368
+ }
369
+ optionArray [ optionArray . Length - 1 ] = new GUIContent ( "Browse..." ) ;
370
+
371
+ return optionArray ;
372
+ }
373
+
374
+ public static void AddMayaOption ( string newOption ) {
375
+ // on OSX we get a path ending in .app, which is not quite the exe
376
+ #if UNITY_EDITOR_OSX
377
+ newOption = GetMayaExePath ( newOption ) ;
378
+ #endif
379
+
380
+ var mayaOptionPaths = instance . mayaOptionPaths ;
381
+ if ( mayaOptionPaths . Contains ( newOption ) ) {
382
+ instance . selectedMayaApp = mayaOptionPaths . IndexOf ( newOption ) ;
383
+ return ;
384
+ }
385
+ // get the version
386
+ var version = AskMayaVersion ( newOption ) ;
387
+ instance . mayaOptionNames . Add ( GetUniqueName ( "Maya " + version ) ) ;
388
+ mayaOptionPaths . Add ( newOption ) ;
389
+ instance . selectedMayaApp = mayaOptionPaths . Count - 1 ;
390
+ }
391
+
392
+ /// <summary>
393
+ /// Ask the version number by running maya.
394
+ /// </summary>
395
+ static string AskMayaVersion ( string exePath ) {
396
+ System . Diagnostics . Process myProcess = new System . Diagnostics . Process ( ) ;
397
+ myProcess . StartInfo . FileName = exePath ;
398
+ myProcess . StartInfo . WindowStyle = System . Diagnostics . ProcessWindowStyle . Hidden ;
399
+ myProcess . StartInfo . CreateNoWindow = true ;
400
+ myProcess . StartInfo . UseShellExecute = false ;
401
+ myProcess . StartInfo . RedirectStandardOutput = true ;
402
+ myProcess . StartInfo . Arguments = "-v" ;
403
+ myProcess . EnableRaisingEvents = true ;
404
+ myProcess . Start ( ) ;
405
+ string resultString = myProcess . StandardOutput . ReadToEnd ( ) ;
406
+ myProcess . WaitForExit ( ) ;
407
+
408
+ // Output is like: Maya 2018, Cut Number 201706261615
409
+ // We want the stuff after 'Maya ' and before the comma.
410
+ // TODO: less brittle! Consider also the mel command "about -version".
411
+ var commaIndex = resultString . IndexOf ( ',' ) ;
412
+ return resultString . Substring ( 0 , commaIndex ) . Substring ( "Maya " . Length ) ;
413
+ }
414
+
415
+ public static string GetSelectedMayaPath ( )
416
+ {
417
+ return instance . mayaOptionPaths [ instance . selectedMayaApp ] ;
158
418
}
159
419
160
420
public static string GetTurnTableSceneName ( ) {
0 commit comments