Skip to content

Commit 32d889a

Browse files
author
AJubrey
committed
Merge branch 'master' into Uni-28914_Adding_an_option_for_launching_after_installation
2 parents 782d148 + 3acb38b commit 32d889a

File tree

9 files changed

+319
-37
lines changed

9 files changed

+319
-37
lines changed

Assets/FbxExporters/Editor/ConvertToModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ public static void UpdateFromSourceRecursive(GameObject dest, GameObject source)
294294
/// <returns>Dictionary containing the name to source game object.</returns>
295295
/// <param name="dest">Destination GameObject.</param>
296296
/// <param name="source">Source GameObject.</param>
297-
private static Dictionary<string,GameObject> MapNameToSourceRecursive(GameObject dest, GameObject source){
297+
public static Dictionary<string,GameObject> MapNameToSourceRecursive(GameObject dest, GameObject source){
298298
var nameToGO = new Dictionary<string,GameObject> ();
299299

300300
var q = new Queue<Transform> ();

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)));
@@ -231,6 +226,12 @@ private static string TryFindDCC(string dccPath, string ext, ExportSettings.DCCT
231226
public class ExportSettings : ScriptableSingleton<ExportSettings>
232227
{
233228
public const string kDefaultSavePath = ".";
229+
private static List<string> s_PreferenceList = new List<string>() {kMayaOptionName, kMayaLtOptionName, kMaxOptionName, kBlenderOptionName };
230+
//Any additional names require a space after the name
231+
public const string kMaxOptionName = "3ds Max ";
232+
public const string kMayaOptionName = "Maya ";
233+
public const string kMayaLtOptionName = "MayaLT ";
234+
public const string kBlenderOptionName = "Blender ";
234235

235236
/// <summary>
236237
/// The path where all the different versions of Maya are installed
@@ -271,7 +272,7 @@ public static string kDefaultAdskRoot {
271272

272273
// List of names in order that they appear in option list
273274
[SerializeField]
274-
private List<string> dccOptionNames;
275+
private List<string> dccOptionNames = new List<string>();
275276
// List of paths in order that they appear in the option list
276277
[SerializeField]
277278
private List<string> dccOptionPaths;
@@ -291,7 +292,12 @@ protected override void LoadDefaults()
291292
/// </summary>
292293
/// <returns>The unique name.</returns>
293294
/// <param name="name">Name.</param>
294-
private static string GetUniqueName(string name){
295+
public static string GetUniqueDCCOptionName(string name){
296+
Debug.Assert(instance != null);
297+
if (name == null)
298+
{
299+
return null;
300+
}
295301
if (!instance.dccOptionNames.Contains(name)) {
296302
return name;
297303
}
@@ -313,11 +319,129 @@ private static string GetUniqueName(string name){
313319
do {
314320
uniqueName = string.Format (format, index, name);
315321
index++;
316-
} while (instance.dccOptionNames.Contains(name));
322+
} while (instance.dccOptionNames.Contains(uniqueName));
317323

318324
return uniqueName;
319325
}
320326

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

361485
if (product.StartsWith ("3ds max", StringComparison.InvariantCultureIgnoreCase)) {
362486
var exePath = string.Format ("{0}/{1}", productDir.FullName.Replace ("\\", "/"), "3dsmax.exe");
363-
if (IsEarlierThanMax2017 (exePath)) {
487+
488+
string version = product.Substring("3ds max ".Length);
489+
var maxOptionName = GetUniqueDCCOptionName(kMaxOptionName + version);
490+
491+
if (IsEarlierThanMax2017 (maxOptionName)) {
364492
continue;
365493
}
366-
string version = product.Substring ("3ds max ".Length);
494+
367495
dccOptionPath.Add (exePath);
368-
dccOptionName.Add (GetUniqueName ("3ds Max " + version));
496+
dccOptionName.Add (maxOptionName);
369497
}
370498
}
499+
instance.selectedDCCApp = instance.GetPreferredDCCApp();
371500
}
372501

373502
/// <summary>
@@ -414,7 +543,7 @@ public static GUIContent[] GetDCCOptions(){
414543
var dccPath = instance.dccOptionPaths [i];
415544
if (!File.Exists (dccPath)) {
416545
if (i == instance.selectedDCCApp) {
417-
instance.selectedDCCApp = 0;
546+
instance.selectedDCCApp = instance.GetPreferredDCCApp();
418547
}
419548
namesToDelete.Add (instance.dccOptionNames [i]);
420549
pathsToDelete.Add (dccPath);
@@ -471,11 +600,20 @@ public static void AddDCCOption(string newOption, DCCType dcc){
471600
Debug.LogError (string.Format("Unity Integration does not support Maya LT: \"{0}\"", newOption));
472601
return;
473602
}
474-
optionName = GetUniqueName ("Maya " + version);
603+
optionName = GetUniqueDCCOptionName("Maya " + version);
475604
break;
476605
case DCCType.Max:
477606
optionName = GetMaxOptionName (newOption);
478-
break;
607+
if (ExportSettings.IsEarlierThanMax2017(optionName))
608+
{
609+
Debug.LogError("Earlier than 3ds Max 2017 is not supported");
610+
UnityEditor.EditorUtility.DisplayDialog(
611+
"Error adding 3D Application",
612+
"Unity Integration only supports 3ds Max 2017 or later",
613+
"Ok");
614+
return;
615+
}
616+
break;
479617
default:
480618
throw new System.NotImplementedException();
481619
}
@@ -514,14 +652,12 @@ static string AskMayaVersion(string exePath) {
514652
/// <returns>The 3DsMax dropdown option label.</returns>
515653
/// <param name="exePath">Exe path.</param>
516654
public static string GetMaxOptionName(string exePath){
517-
return GetUniqueName (Path.GetFileName(Path.GetDirectoryName (exePath)));
655+
return GetUniqueDCCOptionName(Path.GetFileName(Path.GetDirectoryName (exePath)));
518656
}
519657

520-
public static bool IsEarlierThanMax2017(string exePath){
521-
var name = Path.GetFileName (Path.GetDirectoryName (exePath)).ToLower();
522-
name = name.Replace ("3ds max", "").Trim();
523-
int version;
524-
return int.TryParse (name, out version) && version < 2017;
658+
public static bool IsEarlierThanMax2017(string AppName){
659+
int version = FindDCCVersion(AppName);
660+
return version != -1 && version < 2017;
525661
}
526662

527663
public static string GetSelectedDCCPath()

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -585,8 +585,7 @@ void ExportMesh (MeshInfo meshInfo, FbxNode fbxNode)
585585
}
586586
}
587587

588-
int[] unmergedPolygons = new int[meshInfo.Triangles.Length];
589-
int current = 0;
588+
var unmergedPolygons = new List<int> ();
590589
var mesh = meshInfo.mesh;
591590
for (int s = 0; s < mesh.subMeshCount; s++) {
592591
var topology = mesh.GetTopology (s);
@@ -624,12 +623,11 @@ void ExportMesh (MeshInfo meshInfo, FbxNode fbxNode)
624623

625624
// Save the polygon order (without merging vertices) so we
626625
// properly export UVs, normals, binormals, etc.
627-
unmergedPolygons [current] = polyVert;
626+
unmergedPolygons.Add(polyVert);
628627

629628
polyVert = ControlPointToIndex [meshInfo.Vertices [polyVert]];
630629
fbxMesh.AddPolygon (polyVert);
631630

632-
current++;
633631
}
634632
fbxMesh.EndPolygon ();
635633
}
@@ -643,7 +641,7 @@ void ExportMesh (MeshInfo meshInfo, FbxNode fbxNode)
643641
AssignLayerElementMaterial (fbxMesh, meshInfo.mesh, meshInfo.Materials.Length);
644642

645643
// Set up normals, etc.
646-
ExportComponentAttributes (meshInfo, fbxMesh, unmergedPolygons);
644+
ExportComponentAttributes (meshInfo, fbxMesh, unmergedPolygons.ToArray());
647645

648646
// set the fbxNode containing the mesh
649647
fbxNode.SetNodeAttribute (fbxMesh);

Assets/FbxExporters/Editor/UnitTests/ConvertToModelTest.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,56 @@ public void SkinnedMeshTest()
182182
// In the future we'll want to assert the opposite!
183183
Assert.That(cubeInstance.GetComponentsInChildren<SkinnedMeshRenderer>(), Is.Empty);
184184
}
185+
186+
[Test]
187+
public void MapNameToSourceTest()
188+
{
189+
//Create a cube with 3 children game objects
190+
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
191+
var capsule = GameObject.CreatePrimitive(PrimitiveType.Capsule);
192+
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
193+
var quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
194+
195+
capsule.transform.parent = cube.transform;
196+
sphere.transform.parent = cube.transform;
197+
quad.transform.parent = cube.transform;
198+
capsule.transform.SetSiblingIndex(0);
199+
200+
//Create a similar Heirarchy that we can use as our phony "exported" hierarchy.
201+
var cube2 = GameObject.CreatePrimitive(PrimitiveType.Cube);
202+
var capsule2 = GameObject.CreatePrimitive(PrimitiveType.Capsule);
203+
var sphere2 = GameObject.CreatePrimitive(PrimitiveType.Sphere);
204+
var quad2 = GameObject.CreatePrimitive(PrimitiveType.Quad);
205+
206+
capsule2.transform.parent = cube2.transform;
207+
sphere2.transform.parent = cube2.transform;
208+
quad2.transform.parent = cube2.transform;
209+
capsule.transform.SetSiblingIndex(1);
210+
211+
var dictionary = ConvertToModel.MapNameToSourceRecursive(cube, cube2);
212+
213+
//We expect these to pass because we've given it an identical game object, as it would have after a normal export.
214+
Assert.AreSame(capsule2, dictionary[capsule.name]);
215+
Assert.AreSame(sphere2, dictionary[sphere.name]);
216+
Assert.AreSame(quad2, dictionary[quad.name]);
217+
Assert.True(dictionary.Count == 4);
218+
219+
//Create a broken hierarchy, one that is missing a primitive
220+
var cube3 = GameObject.CreatePrimitive(PrimitiveType.Cube);
221+
var capsule3 = GameObject.CreatePrimitive(PrimitiveType.Capsule);
222+
var sphere3 = GameObject.CreatePrimitive(PrimitiveType.Sphere);
223+
224+
capsule3.transform.parent = cube3.transform;
225+
sphere3.transform.parent = cube3.transform;
226+
227+
var dictionaryBroken = ConvertToModel.MapNameToSourceRecursive(cube, cube3);
228+
229+
//the dictionary size should be equal to the amount of children + the parent
230+
Assert.True(dictionaryBroken.Count == 4);
231+
232+
Assert.IsNull(dictionaryBroken[quad.name]);
233+
Assert.AreSame(capsule3, dictionaryBroken[capsule.name]);
234+
Assert.AreSame(sphere3, dictionaryBroken[sphere.name]);
235+
}
185236
}
186237
}

0 commit comments

Comments
 (0)