@@ -36,6 +36,16 @@ public class ModelExporter : System.IDisposable
36
36
37
37
const string ProgressBarTitle = "Fbx Export" ;
38
38
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
+
39
49
/// <summary>
40
50
/// Create instance of example
41
51
/// </summary>
@@ -239,13 +249,13 @@ public void ExportTexture (Material unityMaterial, string unityPropName,
239
249
/// <summary>
240
250
/// Get the color of a material, or grey if we can't find it.
241
251
/// </summary>
242
- public FbxDouble3 GetMaterialColor ( Material unityMaterial , string unityPropName )
252
+ public FbxDouble3 GetMaterialColor ( Material unityMaterial , string unityPropName , float defaultValue = 1 )
243
253
{
244
254
if ( ! unityMaterial ) {
245
- return new FbxDouble3 ( 0.5 ) ;
255
+ return new FbxDouble3 ( defaultValue ) ;
246
256
}
247
257
if ( ! unityMaterial . HasProperty ( unityPropName ) ) {
248
- return new FbxDouble3 ( 0.5 ) ;
258
+ return new FbxDouble3 ( defaultValue ) ;
249
259
}
250
260
var unityColor = unityMaterial . GetColor ( unityPropName ) ;
251
261
return new FbxDouble3 ( unityColor . r , unityColor . g , unityColor . b ) ;
@@ -351,12 +361,14 @@ public void ExportMesh (MeshInfo meshInfo, FbxNode fbxNode, FbxScene fbxScene, b
351
361
}
352
362
fbxMesh . InitControlPoints ( NumControlPoints ) ;
353
363
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.
355
367
foreach ( var controlPoint in ControlPointToIndex . Keys ) {
356
368
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
360
372
) , ControlPointToIndex [ controlPoint ] ) ;
361
373
}
362
374
} else {
@@ -368,9 +380,9 @@ public void ExportMesh (MeshInfo meshInfo, FbxNode fbxNode, FbxScene fbxScene, b
368
380
{
369
381
// convert from left to right-handed by negating x (Unity negates x again on import)
370
382
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
374
386
) , v ) ;
375
387
}
376
388
}
@@ -447,8 +459,13 @@ protected void ExportTransform (UnityEngine.Transform unityTransform, FbxNode fb
447
459
448
460
// transfer transform data from Unity to Fbx
449
461
// 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
+ ) ;
452
469
var fbxRotate = new FbxDouble3 ( unityRotate . x , - unityRotate . y , - unityRotate . z ) ;
453
470
var fbxScale = new FbxDouble3 ( unityScale . x , unityScale . y , unityScale . z ) ;
454
471
@@ -469,6 +486,10 @@ protected int ExportComponents (
469
486
{
470
487
int numObjectsExported = exportProgress ;
471
488
489
+ if ( FbxExporters . EditorTools . ExportSettings . instance . mayaCompatibleNames ) {
490
+ unityGo . name = ConvertToMayaCompatibleName ( unityGo . name ) ;
491
+ }
492
+
472
493
// create an FbxNode and add it as a child of parent
473
494
FbxNode fbxNode = FbxNode . Create ( fbxScene , unityGo . name ) ;
474
495
NumNodes ++ ;
@@ -609,9 +630,11 @@ public int ExportAll (IEnumerable<UnityEngine.Object> unityExportSet)
609
630
fbxSceneInfo . mComment = Comments ;
610
631
fbxScene . SetSceneInfo ( fbxSceneInfo ) ;
611
632
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
613
636
var fbxSettings = fbxScene . GetGlobalSettings ( ) ;
614
- fbxSettings . SetSystemUnit ( FbxSystemUnit . m ) ;
637
+ fbxSettings . SetSystemUnit ( FbxSystemUnit . cm ) ;
615
638
616
639
// The Unity axis system has Y up, Z forward, X to the right (left handed system with odd parity).
617
640
// 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 ()
725
748
}
726
749
727
750
// 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 ) ]
729
755
static void OnContextItem ( MenuCommand command )
730
756
{
731
757
OnExport ( ) ;
@@ -838,6 +864,11 @@ public Material Material {
838
864
if ( ! renderer ) {
839
865
return null ;
840
866
}
867
+
868
+ if ( FbxExporters . EditorTools . ExportSettings . instance . mayaCompatibleNames ) {
869
+ renderer . sharedMaterial . name = ConvertToMayaCompatibleName ( renderer . sharedMaterial . name ) ;
870
+ }
871
+
841
872
// .material instantiates a new material, which is bad
842
873
// most of the time.
843
874
return renderer . sharedMaterial ;
@@ -956,13 +987,19 @@ private static void OnExport ()
956
987
? Application . dataPath
957
988
: System . IO . Path . GetDirectoryName ( LastFilePath ) ;
958
989
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
+ }
962
999
963
1000
var title = string . Format ( "Export Model FBX ({0})" , FileBaseName ) ;
964
1001
965
- var filePath = EditorUtility . SaveFilePanel ( title , directory , filename , "" ) ;
1002
+ var filePath = EditorUtility . SaveFilePanel ( title , directory , filename , "fbx " ) ;
966
1003
967
1004
if ( string . IsNullOrEmpty ( filePath ) ) {
968
1005
return ;
@@ -1011,6 +1048,56 @@ private static void EnsureDirectory (string path)
1011
1048
Directory . CreateDirectory ( fileInfo . Directory . FullName ) ;
1012
1049
}
1013
1050
}
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
+ }
1014
1101
}
1015
1102
}
1016
1103
}
0 commit comments