Skip to content

Commit ce61f06

Browse files
authored
Merge pull request #192 from Unity-Technologies/UNI-27030_Fixing_Default_Program_to_be_most_recent
Uni-27030 fixed bug where most recent version was not the default
2 parents 757e245 + 017dc79 commit ce61f06

File tree

2 files changed

+206
-27
lines changed

2 files changed

+206
-27
lines changed

Assets/FbxExporters/Editor/FbxExportSettings.cs

Lines changed: 163 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,9 @@ public override void OnInspectorGUI() {
9898
var options = ExportSettings.GetDCCOptions();
9999
// make sure we never initially have browse selected
100100
if (exportSettings.selectedDCCApp == options.Length - 1) {
101-
exportSettings.selectedDCCApp = 0;
101+
exportSettings.selectedDCCApp = exportSettings.GetPreferredDCCApp();
102102
}
103+
103104
int oldValue = exportSettings.selectedDCCApp;
104105
exportSettings.selectedDCCApp = EditorGUILayout.Popup(exportSettings.selectedDCCApp, options);
105106
if (exportSettings.selectedDCCApp == options.Length - 1) {
@@ -135,16 +136,10 @@ public override void OnInspectorGUI() {
135136
ExportSettings.DCCType foundDCC = ExportSettings.DCCType.Maya;
136137
var foundDCCPath = TryFindDCC (dccPath, ext, ExportSettings.DCCType.Maya);
137138
if (foundDCCPath == null && Application.platform == RuntimePlatform.WindowsEditor) {
138-
if (ExportSettings.IsEarlierThanMax2017 (dccPath)) {
139-
Debug.LogError ("Earlier than 3ds Max 2017 is not supported");
140-
UnityEditor.EditorUtility.DisplayDialog (
141-
"Error adding 3D Application",
142-
"Unity Integration only supports 3ds Max 2017 or later",
143-
"Ok");
144-
} else {
145-
foundDCCPath = TryFindDCC (dccPath, ext, ExportSettings.DCCType.Max);
146-
foundDCC = ExportSettings.DCCType.Max;
147-
}
139+
140+
foundDCCPath = TryFindDCC (dccPath, ext, ExportSettings.DCCType.Max);
141+
foundDCC = ExportSettings.DCCType.Max;
142+
148143
}
149144
if (foundDCCPath == null) {
150145
Debug.LogError (string.Format ("Could not find supported 3D application at: \"{0}\"", Path.GetDirectoryName (dccPath)));
@@ -225,6 +220,12 @@ private static string TryFindDCC(string dccPath, string ext, ExportSettings.DCCT
225220
public class ExportSettings : ScriptableSingleton<ExportSettings>
226221
{
227222
public const string kDefaultSavePath = ".";
223+
private static List<string> s_PreferenceList = new List<string>() {kMayaOptionName, kMayaLtOptionName, kMaxOptionName, kBlenderOptionName };
224+
//Any additional names require a space after the name
225+
public const string kMaxOptionName = "3ds Max ";
226+
public const string kMayaOptionName = "Maya ";
227+
public const string kMayaLtOptionName = "MayaLT ";
228+
public const string kBlenderOptionName = "Blender ";
228229

229230
/// <summary>
230231
/// The path where all the different versions of Maya are installed
@@ -264,7 +265,7 @@ public static string kDefaultAdskRoot {
264265

265266
// List of names in order that they appear in option list
266267
[SerializeField]
267-
private List<string> dccOptionNames;
268+
private List<string> dccOptionNames = new List<string>();
268269
// List of paths in order that they appear in the option list
269270
[SerializeField]
270271
private List<string> dccOptionPaths;
@@ -283,7 +284,12 @@ protected override void LoadDefaults()
283284
/// </summary>
284285
/// <returns>The unique name.</returns>
285286
/// <param name="name">Name.</param>
286-
private static string GetUniqueName(string name){
287+
public static string GetUniqueDCCOptionName(string name){
288+
Debug.Assert(instance != null);
289+
if (name == null)
290+
{
291+
return null;
292+
}
287293
if (!instance.dccOptionNames.Contains(name)) {
288294
return name;
289295
}
@@ -305,11 +311,129 @@ private static string GetUniqueName(string name){
305311
do {
306312
uniqueName = string.Format (format, index, name);
307313
index++;
308-
} while (instance.dccOptionNames.Contains(name));
314+
} while (instance.dccOptionNames.Contains(uniqueName));
309315

310316
return uniqueName;
311317
}
312318

319+
public void SetDCCOptionNames(List<string> newList)
320+
{
321+
dccOptionNames = newList;
322+
}
323+
324+
public void ClearDCCOptionNames()
325+
{
326+
dccOptionNames.Clear();
327+
}
328+
329+
/// <summary>
330+
///
331+
/// Find the latest program available and make that the default choice.
332+
/// Will always take any Maya version over any 3ds Max version.
333+
///
334+
/// Returns the index of the most recent program in the list of dccOptionNames
335+
///
336+
/// </summary>
337+
public int GetPreferredDCCApp()
338+
{
339+
if (dccOptionNames == null)
340+
{
341+
return -1;
342+
}
343+
344+
int newestDCCVersionIndex = -1;
345+
int newestDCCVersionNumber = -1;
346+
347+
for (int i = 0; i < dccOptionNames.Count; i++)
348+
{
349+
int versionToCheck = FindDCCVersion(dccOptionNames[i]);
350+
if (versionToCheck == -1)
351+
{
352+
continue;
353+
}
354+
if (versionToCheck > newestDCCVersionNumber)
355+
{
356+
newestDCCVersionIndex = i;
357+
newestDCCVersionNumber = versionToCheck;
358+
}
359+
else if (versionToCheck == newestDCCVersionNumber)
360+
{
361+
int selection = ChoosePreferredDCCApp(newestDCCVersionIndex, i);
362+
if (selection == i)
363+
{
364+
newestDCCVersionIndex = i;
365+
newestDCCVersionNumber = FindDCCVersion(dccOptionNames[i]);
366+
}
367+
}
368+
}
369+
Debug.Assert(newestDCCVersionIndex >= -1 && newestDCCVersionIndex < dccOptionNames.Count);
370+
return newestDCCVersionIndex;
371+
}
372+
/// <summary>
373+
/// Takes the index of two program names from dccOptionNames and chooses our preferred one based on the preference list
374+
/// This happens in case of a tie between two programs with the same release year / version
375+
/// </summary>
376+
/// <param name="optionA"></param>
377+
/// <param name="optionB"></param>
378+
/// <returns></returns>
379+
private int ChoosePreferredDCCApp(int optionA, int optionB)
380+
{
381+
Debug.Assert(optionA >= 0 && optionB >= 0 && optionA < dccOptionNames.Count && optionB < dccOptionNames.Count);
382+
if (dccOptionNames.Count == 0)
383+
{
384+
return -1;
385+
}
386+
var appA = dccOptionNames[optionA];
387+
var appB = dccOptionNames[optionB];
388+
if (appA == null || appB == null || appA.Length <= 0 || appB.Length <= 0)
389+
{
390+
return -1;
391+
}
392+
393+
//We assume that the option names have a
394+
int scoreA = s_PreferenceList.IndexOf(appA.Split(' ')[0]);
395+
int scoreB = s_PreferenceList.IndexOf(appB.Split(' ')[0]);
396+
397+
return scoreA < scoreB ? optionA : optionB;
398+
}
399+
400+
/// <summary>
401+
/// Finds the version based off of the title of the application
402+
/// </summary>
403+
/// <param name="path"></param>
404+
/// <returns> the year/version OR -1 if the year could not be parsed </returns>
405+
private static int FindDCCVersion(string AppName)
406+
{
407+
if (AppName == null)
408+
{
409+
return -1;
410+
}
411+
412+
int version;
413+
string[] piecesArray = AppName.Split(' ');
414+
if (piecesArray.Length == 0)
415+
{
416+
return -1;
417+
}
418+
//Get the number, which is always the last chunk separated by a space.
419+
string number = piecesArray[piecesArray.Length - 1];
420+
421+
if (int.TryParse(number, out version))
422+
{
423+
return version;
424+
}
425+
else
426+
{
427+
float fVersion;
428+
//In case we are looking at a Blender version- the int parse will fail so we'll need to parse it as a float.
429+
if (float.TryParse(number, out fVersion))
430+
{
431+
return (int)fVersion;
432+
}
433+
return -1;
434+
}
435+
}
436+
313437
/// <summary>
314438
/// Find Maya and 3DsMax installations at default install path.
315439
/// Add results to given dictionary.
@@ -347,19 +471,24 @@ private static void FindDCCInstalls() {
347471
}
348472
string version = product.Substring ("maya".Length);
349473
dccOptionPath.Add (GetMayaExePath (productDir.FullName.Replace ("\\", "/")));
350-
dccOptionName.Add (GetUniqueName ("Maya " + version));
474+
dccOptionName.Add (GetUniqueDCCOptionName(kMayaOptionName + version));
351475
}
352476

353477
if (product.StartsWith ("3ds max", StringComparison.InvariantCultureIgnoreCase)) {
354478
var exePath = string.Format ("{0}/{1}", productDir.FullName.Replace ("\\", "/"), "3dsmax.exe");
355-
if (IsEarlierThanMax2017 (exePath)) {
479+
480+
string version = product.Substring("3ds max ".Length);
481+
var maxOptionName = GetUniqueDCCOptionName(kMaxOptionName + version);
482+
483+
if (IsEarlierThanMax2017 (maxOptionName)) {
356484
continue;
357485
}
358-
string version = product.Substring ("3ds max ".Length);
486+
359487
dccOptionPath.Add (exePath);
360-
dccOptionName.Add (GetUniqueName ("3ds Max " + version));
488+
dccOptionName.Add (maxOptionName);
361489
}
362490
}
491+
instance.selectedDCCApp = instance.GetPreferredDCCApp();
363492
}
364493

365494
/// <summary>
@@ -406,7 +535,7 @@ public static GUIContent[] GetDCCOptions(){
406535
var dccPath = instance.dccOptionPaths [i];
407536
if (!File.Exists (dccPath)) {
408537
if (i == instance.selectedDCCApp) {
409-
instance.selectedDCCApp = 0;
538+
instance.selectedDCCApp = instance.GetPreferredDCCApp();
410539
}
411540
namesToDelete.Add (instance.dccOptionNames [i]);
412541
pathsToDelete.Add (dccPath);
@@ -463,11 +592,20 @@ public static void AddDCCOption(string newOption, DCCType dcc){
463592
Debug.LogError (string.Format("Unity Integration does not support Maya LT: \"{0}\"", newOption));
464593
return;
465594
}
466-
optionName = GetUniqueName ("Maya " + version);
595+
optionName = GetUniqueDCCOptionName("Maya " + version);
467596
break;
468597
case DCCType.Max:
469598
optionName = GetMaxOptionName (newOption);
470-
break;
599+
if (ExportSettings.IsEarlierThanMax2017(optionName))
600+
{
601+
Debug.LogError("Earlier than 3ds Max 2017 is not supported");
602+
UnityEditor.EditorUtility.DisplayDialog(
603+
"Error adding 3D Application",
604+
"Unity Integration only supports 3ds Max 2017 or later",
605+
"Ok");
606+
return;
607+
}
608+
break;
471609
default:
472610
throw new System.NotImplementedException();
473611
}
@@ -506,14 +644,12 @@ static string AskMayaVersion(string exePath) {
506644
/// <returns>The 3DsMax dropdown option label.</returns>
507645
/// <param name="exePath">Exe path.</param>
508646
public static string GetMaxOptionName(string exePath){
509-
return GetUniqueName (Path.GetFileName(Path.GetDirectoryName (exePath)));
647+
return GetUniqueDCCOptionName(Path.GetFileName(Path.GetDirectoryName (exePath)));
510648
}
511649

512-
public static bool IsEarlierThanMax2017(string exePath){
513-
var name = Path.GetFileName (Path.GetDirectoryName (exePath)).ToLower();
514-
name = name.Replace ("3ds max", "").Trim();
515-
int version;
516-
return int.TryParse (name, out version) && version < 2017;
650+
public static bool IsEarlierThanMax2017(string AppName){
651+
int version = FindDCCVersion(AppName);
652+
return version != -1 && version < 2017;
517653
}
518654

519655
public static string GetSelectedDCCPath()

Assets/FbxExporters/Editor/UnitTests/FbxExportSettingsTest.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,48 @@ public void TestGetSetFields()
178178
var platformPath = Path.Combine("a", Path.Combine("b", "c"));
179179
Assert.AreEqual(Path.Combine(appDataPath, platformPath), ExportSettings.GetAbsoluteSavePath());
180180
}
181+
182+
[Test]
183+
public void TestFindPreferredProgram()
184+
{
185+
//Add a number of fake programs to the list, including some garbage ones
186+
List<string> testList = new List<string>();
187+
testList.Add(null);
188+
testList.Add(ExportSettings.GetUniqueDCCOptionName(ExportSettings.kMaxOptionName + "2000"));
189+
testList.Add(ExportSettings.GetUniqueDCCOptionName(ExportSettings.kMayaOptionName + "2016"));
190+
testList.Add(ExportSettings.GetUniqueDCCOptionName(ExportSettings.kMayaOptionName + "2017"));
191+
testList.Add(ExportSettings.GetUniqueDCCOptionName(ExportSettings.kMaxOptionName + "2017"));
192+
testList.Add(ExportSettings.GetUniqueDCCOptionName(ExportSettings.kBlenderOptionName + "2.67"));
193+
testList.Add(ExportSettings.GetUniqueDCCOptionName(""));
194+
testList.Add(ExportSettings.GetUniqueDCCOptionName(null));
195+
testList.Add(ExportSettings.GetUniqueDCCOptionName(ExportSettings.kMayaLtOptionName));
196+
testList.Add(ExportSettings.GetUniqueDCCOptionName(ExportSettings.kMayaOptionName + "2017"));
197+
198+
ExportSettings.instance.SetDCCOptionNames(testList);
199+
200+
int preferred = ExportSettings.instance.GetPreferredDCCApp();
201+
//While Maya 2017 and 3ds Max 2017 are tied for most recent, Maya 2017 should win because we prefer Maya.
202+
Assert.AreEqual(preferred, 9);
203+
204+
List<string> blenderList = new List<string> { "Blender 2.67", "Blender 3.0" };
205+
ExportSettings.instance.SetDCCOptionNames(blenderList);
206+
207+
preferred = ExportSettings.instance.GetPreferredDCCApp();
208+
//The function should be able to deal with floats well enough to give us a preferred version of soemthing like blender, which does not use the year.
209+
Assert.AreEqual(preferred, 1);
210+
211+
ExportSettings.instance.ClearDCCOptionNames();
212+
//Try running it with an empty list
213+
preferred = ExportSettings.instance.GetPreferredDCCApp();
214+
215+
Assert.AreEqual(preferred, -1);
216+
217+
ExportSettings.instance.SetDCCOptionNames(null);
218+
//Try running it with a null list
219+
preferred = ExportSettings.instance.GetPreferredDCCApp();
220+
221+
Assert.AreEqual(preferred, -1);
222+
}
223+
181224
}
182225
}

0 commit comments

Comments
 (0)