Skip to content

Commit 01bcd4c

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/master' into UNI-21369-convert-model-hierarchy-fix
# Conflicts: # Assets/FbxExporters/Editor/ConvertToModel.cs
2 parents 0ff821f + 5f4dfa0 commit 01bcd4c

File tree

3 files changed

+150
-22
lines changed

3 files changed

+150
-22
lines changed

Assets/FbxExporters/Editor/ConvertToModel.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ private static List<GameObject> OnConvertInPlace (GameObject [] unityActiveGOs)
7878
string dirPath = Path.Combine (Application.dataPath, "Objects");
7979

8080
for(int n = 0; n < gosToExport.Length; n++){
81-
filePaths[n] = Path.Combine (dirPath, gosToExport[n].name + ".fbx");
81+
var filename = ModelExporter.ConvertToValidFilename (gosToExport [n].name + ".fbx");
82+
var filePath = Path.Combine (dirPath, filename);
83+
if (File.Exists (filePath)) {
84+
filePath = IncrementFileName (dirPath, filename);
85+
}
86+
filePaths[n] = filePath;
8287
}
8388

8489
string[] fbxFileNames = new string[filePaths.Length];
@@ -93,7 +98,7 @@ private static List<GameObject> OnConvertInPlace (GameObject [] unityActiveGOs)
9398
{
9499
var fbxFileName = fbxFileNames [i];
95100
if (fbxFileName == null) {
96-
Debug.Log (string.Format ("Warning: Export failed for GameObject {0}", gosToExport [i].name));
101+
Debug.LogWarning (string.Format ("Warning: Export failed for GameObject {0}", gosToExport [i].name));
97102
continue;
98103
}
99104

@@ -110,7 +115,7 @@ private static List<GameObject> OnConvertInPlace (GameObject [] unityActiveGOs)
110115
Object unityMainAsset = AssetDatabase.LoadMainAssetAtPath (fbxFileName);
111116

112117
if (unityMainAsset != null) {
113-
Object unityObj = PrefabUtility.InstantiateAttachedAsset (unityMainAsset);
118+
Object unityObj = PrefabUtility.InstantiatePrefab (unityMainAsset);
114119
GameObject unityGO = unityObj as GameObject;
115120
if (unityGO != null)
116121
{
@@ -138,6 +143,28 @@ private static List<GameObject> OnConvertInPlace (GameObject [] unityActiveGOs)
138143
return result;
139144
}
140145

146+
/// <summary>
147+
/// Check if the file exists, and if it does, then increment the name.
148+
/// e.g. if filename is Sphere.fbx and it already exists, change it to Sphere 1.fbx.
149+
/// </summary>
150+
/// <returns>new file name.</returns>
151+
/// <param name="filename">Filename.</param>
152+
private static string IncrementFileName(string path, string filename)
153+
{
154+
string fileWithoutExt = Path.GetFileNameWithoutExtension (filename);
155+
string ext = Path.GetExtension (filename);
156+
157+
int index = 1;
158+
string file = null;
159+
do {
160+
file = string.Format ("{0} {1}{2}", fileWithoutExt, index, ext);
161+
file = Path.Combine(path, file);
162+
index++;
163+
} while (File.Exists (file));
164+
165+
return file;
166+
}
167+
141168
/// <summary>
142169
/// Enforces that all object names be unique before exporting.
143170
/// If an object with a duplicate name is found, then it is incremented.

Assets/FbxExporters/Editor/FbxExportSettings.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,21 @@ public class ExportSettingsEditor : UnityEditor.Editor {
1111
public override void OnInspectorGUI() {
1212
ExportSettings exportSettings = (ExportSettings)target;
1313

14+
// Increasing the label width so that none of the text gets cut off
15+
EditorGUIUtility.labelWidth = 300;
16+
1417
exportSettings.weldVertices = EditorGUILayout.Toggle ("Weld Vertices:", exportSettings.weldVertices);
1518
exportSettings.embedTextures = EditorGUILayout.Toggle ("Embed Textures:", exportSettings.embedTextures);
19+
exportSettings.mayaCompatibleNames = EditorGUILayout.Toggle (
20+
new GUIContent("Convert to Maya Compatible Naming:",
21+
"In Maya some symbols such as spaces and accents get replaced when importing an FBX " +
22+
"(e.g. \"foo bar\" becomes \"fooFBXASC032bar\"). " +
23+
"On export, convert the names of GameObjects so they are Maya compatible." +
24+
(exportSettings.mayaCompatibleNames? "" :
25+
"\n\nWARNING: Disabling this feature may result in lost material connections," +
26+
" and unexpected character replacements in Maya.")
27+
),
28+
exportSettings.mayaCompatibleNames);
1629

1730
if (GUI.changed) {
1831
EditorUtility.SetDirty (exportSettings);
@@ -26,6 +39,7 @@ public class ExportSettings : FbxExporters.EditorTools.ScriptableSingleton<Expor
2639
{
2740
public bool weldVertices = true;
2841
public bool embedTextures = false;
42+
public bool mayaCompatibleNames = true;
2943

3044
[MenuItem("Edit/Project Settings/Fbx Export", priority = 300)]
3145
static void ShowManager()

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ public class ModelExporter : System.IDisposable
3636

3737
const string ProgressBarTitle = "Fbx Export";
3838

39+
const char MayaNamespaceSeparator = ':';
40+
41+
// replace invalid chars with this one
42+
const char InvalidCharReplacement = '_';
43+
44+
const string RegexCharStart = "[";
45+
const string RegexCharEnd = "]";
46+
47+
const int UnitScaleFactor = 100;
48+
3949
/// <summary>
4050
/// Create instance of example
4151
/// </summary>
@@ -239,13 +249,13 @@ public void ExportTexture (Material unityMaterial, string unityPropName,
239249
/// <summary>
240250
/// Get the color of a material, or grey if we can't find it.
241251
/// </summary>
242-
public FbxDouble3 GetMaterialColor (Material unityMaterial, string unityPropName)
252+
public FbxDouble3 GetMaterialColor (Material unityMaterial, string unityPropName, float defaultValue = 1)
243253
{
244254
if (!unityMaterial) {
245-
return new FbxDouble3 (0.5);
255+
return new FbxDouble3(defaultValue);
246256
}
247257
if (!unityMaterial.HasProperty (unityPropName)) {
248-
return new FbxDouble3 (0.5);
258+
return new FbxDouble3(defaultValue);
249259
}
250260
var unityColor = unityMaterial.GetColor (unityPropName);
251261
return new FbxDouble3 (unityColor.r, unityColor.g, unityColor.b);
@@ -351,12 +361,14 @@ public void ExportMesh (MeshInfo meshInfo, FbxNode fbxNode, FbxScene fbxScene, b
351361
}
352362
fbxMesh.InitControlPoints (NumControlPoints);
353363

354-
// copy control point data from Unity to FBX
364+
// Copy control point data from Unity to FBX.
365+
// As we do so, scale the points by 100 to convert
366+
// from m to cm.
355367
foreach (var controlPoint in ControlPointToIndex.Keys) {
356368
fbxMesh.SetControlPointAt (new FbxVector4 (
357-
-controlPoint.x,
358-
controlPoint.y,
359-
controlPoint.z
369+
-controlPoint.x*UnitScaleFactor,
370+
controlPoint.y*UnitScaleFactor,
371+
controlPoint.z*UnitScaleFactor
360372
), ControlPointToIndex [controlPoint]);
361373
}
362374
} else {
@@ -368,9 +380,9 @@ public void ExportMesh (MeshInfo meshInfo, FbxNode fbxNode, FbxScene fbxScene, b
368380
{
369381
// convert from left to right-handed by negating x (Unity negates x again on import)
370382
fbxMesh.SetControlPointAt(new FbxVector4 (
371-
-meshInfo.Vertices [v].x,
372-
meshInfo.Vertices [v].y,
373-
meshInfo.Vertices [v].z
383+
-meshInfo.Vertices [v].x*UnitScaleFactor,
384+
meshInfo.Vertices [v].y*UnitScaleFactor,
385+
meshInfo.Vertices [v].z*UnitScaleFactor
374386
), v);
375387
}
376388
}
@@ -447,8 +459,13 @@ protected void ExportTransform (UnityEngine.Transform unityTransform, FbxNode fb
447459

448460
// transfer transform data from Unity to Fbx
449461
// Negating the x value of the translation, and the y and z values of the rotation
450-
// to convert from Unity to Maya coordinates (left to righthanded)
451-
var fbxTranslate = new FbxDouble3 (-unityTranslate.x, unityTranslate.y, unityTranslate.z);
462+
// to convert from Unity to Maya coordinates (left to righthanded).
463+
// Scaling the translation by 100 to convert from m to cm.
464+
var fbxTranslate = new FbxDouble3 (
465+
-unityTranslate.x*UnitScaleFactor,
466+
unityTranslate.y*UnitScaleFactor,
467+
unityTranslate.z*UnitScaleFactor
468+
);
452469
var fbxRotate = new FbxDouble3 (unityRotate.x, -unityRotate.y, -unityRotate.z);
453470
var fbxScale = new FbxDouble3 (unityScale.x, unityScale.y, unityScale.z);
454471

@@ -469,6 +486,10 @@ protected int ExportComponents (
469486
{
470487
int numObjectsExported = exportProgress;
471488

489+
if (FbxExporters.EditorTools.ExportSettings.instance.mayaCompatibleNames) {
490+
unityGo.name = ConvertToMayaCompatibleName (unityGo.name);
491+
}
492+
472493
// create an FbxNode and add it as a child of parent
473494
FbxNode fbxNode = FbxNode.Create (fbxScene, unityGo.name);
474495
NumNodes++;
@@ -609,9 +630,11 @@ public int ExportAll (IEnumerable<UnityEngine.Object> unityExportSet)
609630
fbxSceneInfo.mComment = Comments;
610631
fbxScene.SetSceneInfo (fbxSceneInfo);
611632

612-
// Set up the axes (Y up, Z forward, X to the right) and units (meters)
633+
// Set up the axes (Y up, Z forward, X to the right) and units (centimeters)
634+
// Exporting in centimeters as this is the default unit for FBX files, and easiest
635+
// to work with when importing into Maya or Max
613636
var fbxSettings = fbxScene.GetGlobalSettings ();
614-
fbxSettings.SetSystemUnit (FbxSystemUnit.m);
637+
fbxSettings.SetSystemUnit (FbxSystemUnit.cm);
615638

616639
// The Unity axis system has Y up, Z forward, X to the right (left handed system with odd parity).
617640
// The Maya axis system has Y up, Z forward, X to the left (right handed system with odd parity).
@@ -725,7 +748,10 @@ public static bool OnValidateMenuItem ()
725748
}
726749

727750
// Add a menu item called "Export Model..." to a GameObject's context menu.
728-
[MenuItem ("GameObject/Export Model... %e", false, 30)]
751+
// NOTE: The ellipsis at the end of the Menu Item name prevents the context
752+
// from being passed to command, thus resulting in OnContextItem()
753+
// being called only once regardless of what is selected.
754+
[MenuItem ("GameObject/Export Model...", false, 30)]
729755
static void OnContextItem (MenuCommand command)
730756
{
731757
OnExport ();
@@ -838,6 +864,11 @@ public Material Material {
838864
if (!renderer) {
839865
return null;
840866
}
867+
868+
if (FbxExporters.EditorTools.ExportSettings.instance.mayaCompatibleNames) {
869+
renderer.sharedMaterial.name = ConvertToMayaCompatibleName (renderer.sharedMaterial.name);
870+
}
871+
841872
// .material instantiates a new material, which is bad
842873
// most of the time.
843874
return renderer.sharedMaterial;
@@ -956,13 +987,19 @@ private static void OnExport ()
956987
? Application.dataPath
957988
: System.IO.Path.GetDirectoryName (LastFilePath);
958989

959-
var filename = string.IsNullOrEmpty (LastFilePath)
960-
? MakeFileName (basename: FileBaseName, extension: Extension)
961-
: System.IO.Path.GetFileName (LastFilePath);
990+
GameObject [] selectedGOs = Selection.GetFiltered<GameObject> (SelectionMode.TopLevel);
991+
string filename = null;
992+
if (selectedGOs.Length == 1) {
993+
filename = ConvertToValidFilename (selectedGOs [0].name + ".fbx");
994+
} else {
995+
filename = string.IsNullOrEmpty (LastFilePath)
996+
? MakeFileName (basename: FileBaseName, extension: Extension)
997+
: System.IO.Path.GetFileName (LastFilePath);
998+
}
962999

9631000
var title = string.Format ("Export Model FBX ({0})", FileBaseName);
9641001

965-
var filePath = EditorUtility.SaveFilePanel (title, directory, filename, "");
1002+
var filePath = EditorUtility.SaveFilePanel (title, directory, filename, "fbx");
9661003

9671004
if (string.IsNullOrEmpty (filePath)) {
9681005
return;
@@ -1011,6 +1048,56 @@ private static void EnsureDirectory (string path)
10111048
Directory.CreateDirectory (fileInfo.Directory.FullName);
10121049
}
10131050
}
1051+
1052+
/// <summary>
1053+
/// Removes the diacritics (i.e. accents) from letters.
1054+
/// e.g. é becomes e
1055+
/// </summary>
1056+
/// <returns>Text with accents removed.</returns>
1057+
/// <param name="text">Text.</param>
1058+
private static string RemoveDiacritics(string text)
1059+
{
1060+
var normalizedString = text.Normalize(System.Text.NormalizationForm.FormD);
1061+
var stringBuilder = new System.Text.StringBuilder();
1062+
1063+
foreach (var c in normalizedString)
1064+
{
1065+
var unicodeCategory = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c);
1066+
if (unicodeCategory != System.Globalization.UnicodeCategory.NonSpacingMark)
1067+
{
1068+
stringBuilder.Append(c);
1069+
}
1070+
}
1071+
1072+
return stringBuilder.ToString().Normalize(System.Text.NormalizationForm.FormC);
1073+
}
1074+
1075+
private static string ConvertToMayaCompatibleName(string name)
1076+
{
1077+
string newName = RemoveDiacritics (name);
1078+
1079+
if (char.IsDigit (newName [0])) {
1080+
newName = newName.Insert (0, InvalidCharReplacement.ToString());
1081+
}
1082+
1083+
for (int i = 0; i < newName.Length; i++) {
1084+
if (!char.IsLetterOrDigit (newName, i)) {
1085+
if (i < newName.Length-1 && newName [i] == MayaNamespaceSeparator) {
1086+
continue;
1087+
}
1088+
newName = newName.Replace (newName [i], InvalidCharReplacement);
1089+
}
1090+
}
1091+
return newName;
1092+
}
1093+
1094+
public static string ConvertToValidFilename(string filename)
1095+
{
1096+
return System.Text.RegularExpressions.Regex.Replace (filename,
1097+
RegexCharStart + new string(Path.GetInvalidFileNameChars()) + RegexCharEnd,
1098+
InvalidCharReplacement.ToString()
1099+
);
1100+
}
10141101
}
10151102
}
10161103
}

0 commit comments

Comments
 (0)