Skip to content

Commit a33ab2e

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/master' into UNI-29648-1.1.0b1-forum-release
2 parents 7d9f676 + 3acb38b commit a33ab2e

File tree

5 files changed

+261
-33
lines changed

5 files changed

+261
-33
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)));
@@ -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/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)