@@ -62,16 +62,6 @@ public static bool OnValidateMenuItem ()
62
62
return true ;
63
63
}
64
64
65
- /// <summary>
66
- /// After a convert-to-model, we normally delete the original object.
67
- /// That can be overridden.
68
- /// </summary>
69
- public enum KeepOriginal {
70
- Default , // Use the export settings.
71
- Keep , // Don't delete, just rename it.
72
- Delete , // Delete the original hierarchy.
73
- }
74
-
75
65
/// <summary>
76
66
/// Gets the export settings.
77
67
/// </summary>
@@ -89,11 +79,9 @@ public static EditorTools.ExportSettings ExportSettings {
89
79
/// <returns>list of instanced Model Prefabs</returns>
90
80
/// <param name="unityGameObjectsToConvert">Unity game objects to convert to Model Prefab instances</param>
91
81
/// <param name="path">Path to save Model Prefab; use FbxExportSettings if null</param>
92
- /// <param name="keepOriginal">If set to <c>true</c> keep original gameobject hierarchy.</param>
93
82
public static GameObject [ ] CreateInstantiatedModelPrefab (
94
83
GameObject [ ] unityGameObjectsToConvert ,
95
- string directoryFullPath = null ,
96
- KeepOriginal keepOriginal = KeepOriginal . Default )
84
+ string directoryFullPath = null )
97
85
{
98
86
if ( directoryFullPath == null ) {
99
87
directoryFullPath = FbxExporters . EditorTools . ExportSettings . GetAbsoluteSavePath ( ) ;
@@ -106,8 +94,7 @@ public static GameObject[] CreateInstantiatedModelPrefab (
106
94
foreach ( var go in toExport ) {
107
95
try {
108
96
wasExported . Add ( Convert ( go ,
109
- directoryFullPath : directoryFullPath ,
110
- keepOriginal : keepOriginal ) ) ;
97
+ directoryFullPath : directoryFullPath ) ) ;
111
98
} catch ( System . Exception xcp ) {
112
99
Debug . LogException ( xcp ) ;
113
100
}
@@ -121,8 +108,6 @@ public static GameObject[] CreateInstantiatedModelPrefab (
121
108
/// This returns a new object; the converted object may be modified or destroyed.
122
109
///
123
110
/// This refreshes the asset database.
124
- ///
125
- /// If "keepOriginal" is set, the converted object is modified but remains in the scene.
126
111
/// </summary>
127
112
/// <returns>The instance that replaces 'toConvert' in the scene.</returns>
128
113
/// <param name="toConvert">GameObject hierarchy to replace with a prefab.</param>
@@ -134,12 +119,10 @@ public static GameObject[] CreateInstantiatedModelPrefab (
134
119
/// to a directory in which to put the fbx file. Ignored if
135
120
/// fbxFullPath is specified. May be null, in which case we use the
136
121
/// export settings.</param>
137
- /// <param name="keepOriginal">If set to <c>true</c>, keep the original in the scene.</param>
138
122
public static GameObject Convert (
139
123
GameObject toConvert ,
140
124
string directoryFullPath = null ,
141
- string fbxFullPath = null ,
142
- KeepOriginal keepOriginal = KeepOriginal . Default )
125
+ string fbxFullPath = null )
143
126
{
144
127
if ( string . IsNullOrEmpty ( fbxFullPath ) ) {
145
128
// Generate a unique filename.
@@ -182,61 +165,29 @@ public static GameObject Convert (
182
165
throw new System . Exception ( "Failed to convert " + toConvert . name ) ; ;
183
166
}
184
167
185
- // Instantiate the FBX file.
186
- var unityGO = PrefabUtility . InstantiatePrefab ( unityMainAsset , toConvert . scene )
187
- as GameObject ;
188
- if ( ! unityGO ) {
189
- throw new System . Exception ( "Failed to convert " + toConvert . name ) ; ;
190
- }
191
-
192
- // Copy the components over to the instance of the FBX.
193
- SetupImportedGameObject ( toConvert , unityGO ) ;
168
+ // Copy the mesh/materials from the FBX
169
+ UpdateFromSourceRecursive ( toConvert , unityMainAsset ) ;
194
170
195
171
// Set up the FbxPrefab component so it will auto-update.
196
172
// Make sure to delete whatever FbxPrefab history we had.
197
- var fbxPrefab = unityGO . GetComponent < FbxPrefab > ( ) ;
173
+ var fbxPrefab = toConvert . GetComponent < FbxPrefab > ( ) ;
198
174
if ( fbxPrefab ) {
199
175
Object . DestroyImmediate ( fbxPrefab ) ;
200
176
}
201
- fbxPrefab = unityGO . AddComponent < FbxPrefab > ( ) ;
177
+ fbxPrefab = toConvert . AddComponent < FbxPrefab > ( ) ;
202
178
var fbxPrefabUtility = new FbxPrefabAutoUpdater . FbxPrefabUtility ( fbxPrefab ) ;
203
179
fbxPrefabUtility . SetSourceModel ( unityMainAsset ) ;
204
180
205
- // Disconnect from the FBX file.
206
- PrefabUtility . DisconnectPrefabInstance ( unityGO ) ;
207
-
208
181
// Create a prefab from the instantiated and componentized unityGO.
209
182
var prefabFileName = Path . ChangeExtension ( projectRelativePath , ".prefab" ) ;
210
- var prefab = PrefabUtility . CreatePrefab ( prefabFileName , unityGO , ReplacePrefabOptions . ConnectToPrefab ) ;
183
+ var prefab = PrefabUtility . CreatePrefab ( prefabFileName , toConvert , ReplacePrefabOptions . ConnectToPrefab ) ;
211
184
if ( ! prefab ) {
212
185
throw new System . Exception (
213
186
string . Format ( "Failed to create prefab asset in [{0}] from fbx [{1}]" ,
214
187
prefabFileName , fbxFullPath ) ) ;
215
188
}
216
189
217
- // Remove (now redundant) gameobject
218
- bool actuallyKeepOriginal ;
219
- switch ( keepOriginal ) {
220
- case KeepOriginal . Delete :
221
- actuallyKeepOriginal = false ;
222
- break ;
223
- case KeepOriginal . Keep :
224
- actuallyKeepOriginal = true ;
225
- break ;
226
- case KeepOriginal . Default :
227
- default :
228
- actuallyKeepOriginal = ExportSettings . keepOriginalAfterConvert ;
229
- break ;
230
- }
231
- if ( ! actuallyKeepOriginal ) {
232
- Object . DestroyImmediate ( toConvert ) ;
233
- } else {
234
- // rename and put under scene root in case we need to check values
235
- toConvert . name = "_safe_to_delete_" + toConvert . name ;
236
- toConvert . SetActive ( false ) ;
237
- }
238
-
239
- return unityGO ;
190
+ return toConvert ;
240
191
}
241
192
242
193
/// <summary>
@@ -311,91 +262,87 @@ public static void EnforceUniqueNames(IEnumerable<GameObject> exportSet)
311
262
}
312
263
313
264
/// <summary>
314
- /// Sets up the imported GameObject to match the original.
315
- /// - Updates the name to be the same as original (i.e. remove the "(Clone)")
316
- /// - Moves the imported object to the correct position in the hierarchy
317
- /// - Updates the transform of the imported GameObject to match the original
318
- /// - Copy over missing components and component values
265
+ /// Updates the meshes and materials of the exported GameObjects
266
+ /// to link to those imported from the FBX.
319
267
/// </summary>
320
- /// <param name="orig">Original GameObject.</param>
321
- /// <param name="imported">Imported GameObject .</param>
322
- public static void SetupImportedGameObject ( GameObject orig , GameObject imported )
268
+ /// <param name="dest"> GameObject to update .</param>
269
+ /// <param name="source">Source to update from .</param>
270
+ public static void UpdateFromSourceRecursive ( GameObject dest , GameObject source )
323
271
{
324
- Transform importedTransform = imported . transform ;
325
- Transform origTransform = orig . transform ;
326
-
327
- // configure transform and maintain local pose
328
- importedTransform . SetParent ( origTransform . parent , false ) ;
329
- importedTransform . SetSiblingIndex ( origTransform . GetSiblingIndex ( ) ) ;
330
-
331
- // copy the components over, assuming that the hierarchy order is unchanged
332
- if ( origTransform . hierarchyCount != importedTransform . hierarchyCount ) {
333
- Debug . LogWarning ( string . Format ( "Warning: Exported {0} objects, but only imported {1}" ,
334
- origTransform . hierarchyCount , importedTransform . hierarchyCount ) ) ;
272
+ // recurse over orig, for each transform finding the corresponding transform in the FBX
273
+ // and copying the meshes and materials over from the FBX
274
+ var goDict = MapNameToSourceRecursive ( dest , source ) ;
275
+ var q = new Queue < Transform > ( ) ;
276
+ q . Enqueue ( dest . transform ) ;
277
+ while ( q . Count > 0 ) {
278
+ var t = q . Dequeue ( ) ;
279
+
280
+ if ( goDict [ t . name ] == null ) {
281
+ Debug . LogWarning ( string . Format ( "Warning: Could not find Object {0} in FBX" , t . name ) ) ;
282
+ continue ;
283
+ }
284
+ CopyComponents ( t . gameObject , goDict [ t . name ] ) ;
285
+ foreach ( Transform child in t ) {
286
+ q . Enqueue ( child ) ;
287
+ }
335
288
}
336
- FixSiblingOrder ( orig . transform , imported . transform ) ;
337
-
338
- // the imported GameObject will have the same name as the file to which it was imported from,
339
- // which might not be the same name as the original GameObject
340
- CopyComponentsRecursive ( orig , imported , namesExpectedMatch : false ) ;
341
289
}
342
290
343
291
/// <summary>
344
- /// Given two hierarchies of nodes whose names match up,
345
- /// make the 'imported' hierarchy have its children be in the same
346
- /// order as the 'orig' hierarchy.
347
- ///
348
- /// The 'orig' hierarchy is not modified.
292
+ /// Gets a dictionary linking dest GameObject name to source game object.
349
293
/// </summary>
350
- public static void FixSiblingOrder ( Transform orig , Transform imported ) {
351
- foreach ( Transform origChild in orig ) {
352
- Transform importedChild = imported . Find ( origChild . name ) ;
353
- if ( importedChild == null ) {
354
- Debug . LogWarning ( string . Format (
355
- "Warning: Could not find {0} in parented under {1} in import hierarchy" ,
356
- origChild . name , imported . name
357
- ) ) ;
358
- continue ;
294
+ /// <returns>Dictionary containing the name to source game object.</returns>
295
+ /// <param name="dest">Destination GameObject.</param>
296
+ /// <param name="source">Source GameObject.</param>
297
+ private static Dictionary < string , GameObject > MapNameToSourceRecursive ( GameObject dest , GameObject source ) {
298
+ var nameToGO = new Dictionary < string , GameObject > ( ) ;
299
+
300
+ var q = new Queue < Transform > ( ) ;
301
+ q . Enqueue ( dest . transform ) ;
302
+ while ( q . Count > 0 ) {
303
+ var t = q . Dequeue ( ) ;
304
+ nameToGO [ t . name ] = null ;
305
+ foreach ( Transform child in t ) {
306
+ q . Enqueue ( child ) ;
359
307
}
360
- importedChild . SetSiblingIndex ( origChild . GetSiblingIndex ( ) ) ;
361
- FixSiblingOrder ( origChild , importedChild ) ;
362
308
}
363
- }
364
309
365
- private static void CopyComponentsRecursive ( GameObject from , GameObject to , bool namesExpectedMatch = true ) {
366
- if ( namesExpectedMatch && ! to . name . StartsWith ( from . name ) || from . transform . childCount != to . transform . childCount ) {
367
- Debug . LogError ( string . Format ( "Error: hierarchies don't match (From: {0}, To: {1})" , from . name , to . name ) ) ;
368
- return ;
310
+ nameToGO [ dest . name ] = source ;
311
+
312
+ var fbxQ = new Queue < Transform > ( ) ;
313
+ foreach ( Transform child in source . transform ) {
314
+ fbxQ . Enqueue ( child ) ;
369
315
}
370
316
371
- CopyComponents ( from , to ) ;
372
- for ( int i = 0 ; i < from . transform . childCount ; i ++ ) {
373
- CopyComponentsRecursive ( from . transform . GetChild ( i ) . gameObject , to . transform . GetChild ( i ) . gameObject ) ;
317
+ while ( fbxQ . Count > 0 ) {
318
+ var t = fbxQ . Dequeue ( ) ;
319
+ if ( ! nameToGO . ContainsKey ( t . name ) ) {
320
+ Debug . LogWarning ( string . Format ( "Warning: {0} in FBX but not in converted hierarchy" , t . name ) ) ;
321
+ continue ;
322
+ }
323
+ nameToGO [ t . name ] = t . gameObject ;
324
+ foreach ( Transform child in t ) {
325
+ fbxQ . Enqueue ( child ) ;
326
+ }
374
327
}
328
+
329
+ return nameToGO ;
375
330
}
376
331
377
332
/// <summary>
378
- /// Copy components on the 'from' object which is the object in the
379
- /// scene we exported, over to the 'to' object which is the object
380
- /// in the scene that we imported from the FBX .
333
+ /// Copy components on the 'from' object which is the FBX,
334
+ /// over to the 'to' object which is the object in the
335
+ /// scene we exported .
381
336
///
382
- /// Exception: don't copy the references to assets in the scene that
383
- /// were also exported, in particular the meshes and materials .
337
+ /// Only copy over meshes and materials, since that is all the FBX contains
338
+ /// that is not already in the scene .
384
339
///
385
340
/// The 'from' hierarchy is not modified.
386
341
/// </summary>
387
- public static void CopyComponents ( GameObject from , GameObject to ) {
342
+ public static void CopyComponents ( GameObject to , GameObject from ) {
388
343
var originalComponents = new List < Component > ( to . GetComponents < Component > ( ) ) ;
389
- foreach ( var component in from . GetComponents < Component > ( ) ) {
390
- // UNI-24379: we don't support SkinnedMeshRenderer right
391
- // now: we just bake the mesh into its current pose. So
392
- // don't copy the SMR over. There will already be a
393
- // MeshFilter and MeshRenderer due to the static mesh.
394
- // Remove this code when we support skinning.
395
- if ( component is SkinnedMeshRenderer ) {
396
- continue ;
397
- }
398
-
344
+ // copy over meshes, materials, and nothing else
345
+ foreach ( var component in from . GetComponents < Component > ( ) ) {
399
346
var json = EditorJsonUtility . ToJson ( component ) ;
400
347
if ( string . IsNullOrEmpty ( json ) ) {
401
348
// this happens for missing scripts
@@ -417,40 +364,36 @@ public static void CopyComponents(GameObject from, GameObject to){
417
364
}
418
365
419
366
if ( ! toComponent ) {
420
- // It doesn't exist => create and copy.
421
- toComponent = to . AddComponent ( component . GetType ( ) ) ;
422
- if ( toComponent ) {
423
- EditorJsonUtility . FromJsonOverwrite ( json , toComponent ) ;
424
- }
425
- } else {
426
- // It exists => copy.
427
- // But we want to override that behaviour in a few
428
- // cases, to avoid clobbering references to the new FBX
429
- // TODO: interpret the object or the json more directly
430
- // TODO: be more generic
431
- // TODO: handle references to other objects in the same hierarchy
432
-
433
- if ( toComponent is MeshFilter ) {
434
- // Don't copy the mesh. But there's nothing else to
435
- // copy, so just don't copy anything.
436
- } else if ( toComponent is SkinnedMeshRenderer ) {
437
- // Don't want to clobber materials or the mesh.
438
- var skinnedMesh = toComponent as SkinnedMeshRenderer ;
439
- var sharedMesh = skinnedMesh . sharedMesh ;
440
- var sharedMats = skinnedMesh . sharedMaterials ;
441
- EditorJsonUtility . FromJsonOverwrite ( json , toComponent ) ;
442
- skinnedMesh . sharedMesh = sharedMesh ;
443
- skinnedMesh . sharedMaterials = sharedMats ;
444
- } else if ( toComponent is Renderer ) {
445
- // Don't want to clobber materials.
446
- var renderer = toComponent as Renderer ;
447
- var sharedMats = renderer . sharedMaterials ;
448
- EditorJsonUtility . FromJsonOverwrite ( json , toComponent ) ;
449
- renderer . sharedMaterials = sharedMats ;
450
- } else {
451
- // Normal case: copy everything.
452
- EditorJsonUtility . FromJsonOverwrite ( json , toComponent ) ;
367
+ // copy over mesh filter and mesh renderer to replace
368
+ // skinned mesh renderer
369
+ if ( component is MeshFilter ) {
370
+ var skinnedMesh = to . GetComponent < SkinnedMeshRenderer > ( ) ;
371
+ if ( skinnedMesh ) {
372
+ toComponent = to . AddComponent ( component . GetType ( ) ) ;
373
+ EditorJsonUtility . FromJsonOverwrite ( json , toComponent ) ;
374
+
375
+ var toRenderer = to . AddComponent < MeshRenderer > ( ) ;
376
+ var fromRenderer = from . GetComponent < MeshRenderer > ( ) ;
377
+ if ( toRenderer && fromRenderer ) {
378
+ EditorJsonUtility . FromJsonOverwrite ( EditorJsonUtility . ToJson ( fromRenderer ) , toRenderer ) ;
379
+ }
380
+ Object . DestroyImmediate ( skinnedMesh ) ;
381
+ }
453
382
}
383
+ continue ;
384
+ }
385
+
386
+ if ( toComponent is SkinnedMeshRenderer ) {
387
+ var skinnedMesh = toComponent as SkinnedMeshRenderer ;
388
+ var fromSkinnedMesh = component as SkinnedMeshRenderer ;
389
+ skinnedMesh . sharedMesh = fromSkinnedMesh . sharedMesh ;
390
+ skinnedMesh . sharedMaterials = fromSkinnedMesh . sharedMaterials ;
391
+ } else if ( toComponent is MeshFilter ) {
392
+ EditorJsonUtility . FromJsonOverwrite ( json , toComponent ) ;
393
+ } else if ( toComponent is Renderer ) {
394
+ var toRenderer = toComponent as Renderer ;
395
+ var fromRenderer = component as Renderer ;
396
+ toRenderer . sharedMaterials = fromRenderer . sharedMaterials ;
454
397
}
455
398
}
456
399
}
0 commit comments