@@ -32,7 +32,8 @@ public void Dispose () { }
32
32
[ MenuItem ( MenuItemName1 , false ) ]
33
33
public static void OnMenuItem ( )
34
34
{
35
- OnConvertInPlace ( ) ;
35
+ GameObject [ ] unityActiveGOs = Selection . GetFiltered < GameObject > ( SelectionMode . Editable | SelectionMode . TopLevel ) ;
36
+ OnConvertInPlace ( unityActiveGOs ) ;
36
37
}
37
38
38
39
/// <summary>
@@ -45,28 +46,44 @@ public static bool OnValidateMenuItem ()
45
46
}
46
47
47
48
// Add a menu item called "Export Model..." to a GameObject's context menu.
49
+ // OnContextItem gets called once per selected object
50
+ // (if the parent and child are selected, then OnContextItem will only be called on the parent)
48
51
[ MenuItem ( MenuItemName2 , false , 30 ) ]
49
52
static void OnContextItem ( MenuCommand command )
50
53
{
51
- OnConvertInPlace ( ) ;
54
+ if ( command == null || command . context == null ) {
55
+ Debug . LogError ( "Error: No GameObject selected" ) ;
56
+ return ;
57
+ }
58
+ GameObject selected = command . context as GameObject ;
59
+ if ( selected == null ) {
60
+ Debug . LogError ( string . Format ( "Error: {0} is not a GameObject and cannot be converted" , command . context . name ) ) ;
61
+ return ;
62
+ }
63
+ OnConvertInPlace ( new GameObject [ ] { selected } ) ;
52
64
}
53
65
54
- private static List < GameObject > OnConvertInPlace ( )
66
+ private static List < GameObject > OnConvertInPlace ( GameObject [ ] unityActiveGOs )
55
67
{
56
68
List < GameObject > result = new List < GameObject > ( ) ;
57
69
58
- GameObject [ ] unityActiveGOs = Selection . GetFiltered < GameObject > ( SelectionMode . Editable | SelectionMode . TopLevel ) ;
59
-
60
70
var exportSet = ModelExporter . RemoveRedundantObjects ( unityActiveGOs ) ;
61
71
GameObject [ ] gosToExport = new GameObject [ exportSet . Count ] ;
62
72
exportSet . CopyTo ( gosToExport ) ;
63
73
74
+ EnforceUniqueNames ( gosToExport ) ;
75
+
64
76
// find common ancestor root & filePath;
65
77
string [ ] filePaths = new string [ gosToExport . Length ] ;
66
78
string dirPath = Path . Combine ( Application . dataPath , "Objects" ) ;
67
79
68
80
for ( int n = 0 ; n < gosToExport . Length ; n ++ ) {
69
- 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 ;
70
87
}
71
88
72
89
string [ ] fbxFileNames = new string [ filePaths . Length ] ;
@@ -81,7 +98,7 @@ private static List<GameObject> OnConvertInPlace ()
81
98
{
82
99
var fbxFileName = fbxFileNames [ i ] ;
83
100
if ( fbxFileName == null ) {
84
- 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 ) ) ;
85
102
continue ;
86
103
}
87
104
@@ -98,7 +115,7 @@ private static List<GameObject> OnConvertInPlace ()
98
115
Object unityMainAsset = AssetDatabase . LoadMainAssetAtPath ( fbxFileName ) ;
99
116
100
117
if ( unityMainAsset != null ) {
101
- Object unityObj = PrefabUtility . InstantiateAttachedAsset ( unityMainAsset ) ;
118
+ Object unityObj = PrefabUtility . InstantiatePrefab ( unityMainAsset ) ;
102
119
GameObject unityGO = unityObj as GameObject ;
103
120
if ( unityGO != null )
104
121
{
@@ -126,6 +143,66 @@ private static List<GameObject> OnConvertInPlace ()
126
143
return result ;
127
144
}
128
145
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
+
168
+ /// <summary>
169
+ /// Enforces that all object names be unique before exporting.
170
+ /// If an object with a duplicate name is found, then it is incremented.
171
+ /// e.g. Sphere becomes Sphere 1
172
+ /// </summary>
173
+ /// <param name="exportSet">Export set.</param>
174
+ private static void EnforceUniqueNames ( GameObject [ ] exportSet )
175
+ {
176
+ Dictionary < string , int > NameToIndexMap = new Dictionary < string , int > ( ) ;
177
+ string format = "{0} {1}" ;
178
+
179
+ Queue < GameObject > queue = new Queue < GameObject > ( exportSet ) ;
180
+
181
+ while ( queue . Count > 0 ) {
182
+ var go = queue . Dequeue ( ) ;
183
+ var name = go . name ;
184
+ if ( NameToIndexMap . ContainsKey ( name ) ) {
185
+ go . name = string . Format ( format , name , NameToIndexMap [ name ] ) ;
186
+ NameToIndexMap [ name ] ++ ;
187
+ } else {
188
+ NameToIndexMap [ name ] = 1 ;
189
+ }
190
+
191
+ foreach ( Transform child in go . transform ) {
192
+ queue . Enqueue ( child . gameObject ) ;
193
+ }
194
+ }
195
+ }
196
+
197
+ /// <summary>
198
+ /// Sets up the imported GameObject to match the original.
199
+ /// - Updates the name to be the same as original (i.e. remove the "(Clone)")
200
+ /// - Moves the imported object to the correct position in the hierarchy
201
+ /// - Updates the transform of the imported GameObject to match the original
202
+ /// - Copy over missing components and component values
203
+ /// </summary>
204
+ /// <param name="orig">Original GameObject.</param>
205
+ /// <param name="imported">Imported GameObject.</param>
129
206
private static void SetupImportedGameObject ( GameObject orig , GameObject imported )
130
207
{
131
208
Transform importedTransform = imported . transform ;
@@ -154,9 +231,25 @@ private static void SetupImportedGameObject(GameObject orig, GameObject imported
154
231
Debug . LogWarning ( string . Format ( "Warning: Exported {0} objects, but only imported {1}" ,
155
232
origTransform . hierarchyCount , importedTransform . hierarchyCount ) ) ;
156
233
}
234
+ FixSiblingOrder ( orig . transform , imported . transform ) ;
157
235
CopyComponentsRecursive ( orig , imported ) ;
158
236
}
159
237
238
+ private static void FixSiblingOrder ( Transform orig , Transform imported ) {
239
+ foreach ( Transform origChild in orig ) {
240
+ Transform importedChild = imported . Find ( origChild . name ) ;
241
+ if ( importedChild == null ) {
242
+ Debug . LogWarning ( string . Format (
243
+ "Warning: Could not find {0} in parented under {1} in import hierarchy" ,
244
+ origChild . name , imported . name
245
+ ) ) ;
246
+ continue ;
247
+ }
248
+ importedChild . SetSiblingIndex ( origChild . GetSiblingIndex ( ) ) ;
249
+ FixSiblingOrder ( origChild , importedChild ) ;
250
+ }
251
+ }
252
+
160
253
private static void CopyComponentsRecursive ( GameObject from , GameObject to ) {
161
254
if ( ! to . name . StartsWith ( from . name ) || from . transform . childCount != to . transform . childCount ) {
162
255
Debug . LogError ( string . Format ( "Error: hierarchies don't match (From: {0}, To: {1})" , from . name , to . name ) ) ;
0 commit comments