@@ -12,7 +12,8 @@ namespace Editor
12
12
{
13
13
public static class ConvertToModel
14
14
{
15
- const string MenuItemName1 = "GameObject/Convert To Linked Prefab Instance..." ;
15
+ const string GameObjectMenuItemName = "GameObject/Convert To Linked Prefab Instance..." ;
16
+ const string AssetsMenuItemName = "Assets/Convert To Linked Prefab..." ;
16
17
17
18
/// <summary>
18
19
/// OnContextItem is called either:
@@ -23,14 +24,22 @@ public static class ConvertToModel
23
24
/// parent and child are selected, then OnContextItem will only be
24
25
/// called on the parent)
25
26
/// </summary>
26
- [ MenuItem ( MenuItemName1 , false , 30 ) ]
27
- static void OnContextItem ( MenuCommand command )
27
+ [ MenuItem ( GameObjectMenuItemName , false , 30 ) ]
28
+ static void OnGameObjectContextItem ( MenuCommand command ) {
29
+ OnContextItem ( command , SelectionMode . Editable | SelectionMode . TopLevel ) ;
30
+ }
31
+ [ MenuItem ( AssetsMenuItemName , false , 30 ) ]
32
+ static void OnAssetsContextItem ( MenuCommand command ) {
33
+ OnContextItem ( command , SelectionMode . Assets ) ;
34
+ }
35
+
36
+ static void OnContextItem ( MenuCommand command , SelectionMode mode )
28
37
{
29
38
GameObject [ ] selection = null ;
30
39
31
40
if ( command == null || command . context == null ) {
32
41
// We were actually invoked from the top GameObject menu, so use the selection.
33
- selection = Selection . GetFiltered < GameObject > ( SelectionMode . Editable | SelectionMode . TopLevel ) ;
42
+ selection = Selection . GetFiltered < GameObject > ( mode ) ;
34
43
} else {
35
44
// We were invoked from the right-click menu, so use the context of the context menu.
36
45
var selected = command . context as GameObject ;
@@ -48,9 +57,10 @@ static void OnContextItem (MenuCommand command)
48
57
}
49
58
50
59
/// <summary>
51
- // Validate the menu item defined by the function above.
60
+ // Validate the menu items defined above.
52
61
/// </summary>
53
- [ MenuItem ( MenuItemName1 , true , 30 ) ]
62
+ [ MenuItem ( GameObjectMenuItemName , true , 30 ) ]
63
+ [ MenuItem ( AssetsMenuItemName , true , 30 ) ]
54
64
public static bool OnValidateMenuItem ( )
55
65
{
56
66
return true ;
@@ -87,8 +97,11 @@ public static GameObject[] CreateInstantiatedModelPrefab (
87
97
/// This returns a new object; the converted object may be modified or destroyed.
88
98
///
89
99
/// This refreshes the asset database.
100
+ ///
101
+ /// If 'toConvert' is an asset, we return a prefab.
102
+ /// If 'toConvert' is an object in the scene, we return an instance of the prefab.
90
103
/// </summary>
91
- /// <returns>The instance that replaces 'toConvert' in the scene.</returns>
104
+ /// <returns>The instance that replaces 'toConvert' in the scene, or the prefab that replaces 'toConvert' in the project .</returns>
92
105
/// <param name="toConvert">GameObject hierarchy to replace with a prefab.</param>
93
106
/// <param name="fbxFullPath">Absolute platform-specific path to
94
107
/// the fbx file. May be null, in which case we construct a unique
@@ -107,18 +120,15 @@ public static GameObject Convert (
107
120
EditorTools . IExportOptions exportOptions = null
108
121
)
109
122
{
110
- // Only create the prefab (no FBX export) if we have selected the root of a model prefab instance.
111
- if ( IsModelInstance ( toConvert ) ) {
112
- // don't re-export fbx
113
- // create prefab out of model instance in scene, link to existing fbx
114
- var mainAsset = PrefabUtility . GetPrefabParent ( toConvert ) as GameObject ;
123
+ // If we selected the something that's already backed by an FBX, don't export.
124
+ var mainAsset = GetModelPrefabOrNull ( toConvert ) ;
125
+ if ( mainAsset ) {
115
126
var mainAssetRelPath = AssetDatabase . GetAssetPath ( mainAsset ) ;
116
- var mainAssetAbsPath = Directory . GetParent ( Application . dataPath ) + "/" + mainAssetRelPath ;
117
- SetupFbxPrefab ( toConvert , mainAsset , mainAssetRelPath , mainAssetAbsPath ) ;
118
-
119
- return toConvert ;
127
+ return SetupFbxPrefab ( toConvert , mainAsset , mainAssetRelPath ) ;
120
128
}
121
129
130
+ // Otherwise, do export.
131
+
122
132
if ( string . IsNullOrEmpty ( fbxFullPath ) ) {
123
133
// Generate a unique filename.
124
134
if ( string . IsNullOrEmpty ( fbxDirectoryFullPath ) ) {
@@ -178,20 +188,39 @@ public static GameObject Convert (
178
188
}
179
189
var prefabProjectRelativePath = EditorTools . ExportSettings . GetProjectRelativePath ( prefabFullPath ) ;
180
190
181
- SetupFbxPrefab ( toConvert , unityMainAsset , prefabProjectRelativePath , fbxFullPath ) ;
182
-
191
+ // Create the FbxPrefab component and the prefab asset itself.
192
+ toConvert = SetupFbxPrefab ( toConvert , unityMainAsset , prefabProjectRelativePath ) ;
183
193
toConvert . name = Path . GetFileNameWithoutExtension ( fbxFullPath ) ;
194
+
184
195
return toConvert ;
185
196
}
186
197
187
198
/// <summary>
188
- /// Create the prefab and connect it to the given fbx asset.
199
+ /// Connect 'toConvert' to the main asset and create a prefab on disk.
200
+ ///
201
+ /// If 'toConvert' is an asset, we return a prefab.
202
+ /// If 'toConvert' is an object in the scene, we return an instance of the prefab.
189
203
/// </summary>
190
204
/// <param name="toConvert">Hierarchy to convert.</param>
191
205
/// <param name="unityMainAsset">Main asset in the FBX.</param>
192
206
/// <param name="projectRelativePath">Fbx project relative path.</param>
193
- /// <param name="fbxFullPath">Fbx full path.</param>
194
- public static void SetupFbxPrefab ( GameObject toConvert , GameObject unityMainAsset , string projectRelativePath , string fbxFullPath ) {
207
+ public static GameObject SetupFbxPrefab ( GameObject toConvert , GameObject unityMainAsset , string projectRelativePath )
208
+ {
209
+ if ( PrefabUtility . GetPrefabType ( toConvert ) == PrefabType . ModelPrefab ) {
210
+ // We can't directly edit the prefab. Instead we must:
211
+ // Instantiate, set up the instance, then destroy the
212
+ // instance but return the prefab.
213
+ GameObject instance = null ;
214
+ try {
215
+ // Object.Instantiate so that we can get just the branch we care about.
216
+ instance = Object . Instantiate ( toConvert ) ;
217
+ instance = SetupFbxPrefab ( instance , unityMainAsset , projectRelativePath ) ;
218
+ return PrefabUtility . GetPrefabParent ( instance ) as GameObject ;
219
+ } finally {
220
+ if ( instance ) { Object . DestroyImmediate ( instance ) ; }
221
+ }
222
+ }
223
+
195
224
// Set up the FbxPrefab component so it will auto-update.
196
225
// Make sure to delete whatever FbxPrefab history we had.
197
226
var fbxPrefab = toConvert . GetComponent < FbxPrefab > ( ) ;
@@ -206,18 +235,25 @@ public static void SetupFbxPrefab(GameObject toConvert, GameObject unityMainAsse
206
235
var prefabFileName = Path . ChangeExtension ( projectRelativePath , ".prefab" ) ;
207
236
var prefab = PrefabUtility . CreatePrefab ( prefabFileName , toConvert , ReplacePrefabOptions . ConnectToPrefab ) ;
208
237
if ( ! prefab ) {
238
+ var fbxFullPath = AssetDatabase . GetAssetPath ( unityMainAsset ) ;
209
239
throw new System . Exception (
210
240
string . Format ( "Failed to create prefab asset in [{0}] from fbx [{1}]" ,
211
241
prefabFileName , fbxFullPath ) ) ;
212
242
}
243
+
244
+ return toConvert ;
213
245
}
214
246
215
247
/// <summary>
216
- /// Determines if the given GameObject is a model instance.
248
+ /// Returns the prefab on disk corresponding to the same hierarchy as is selected.
249
+ ///
250
+ /// Returns go if go is the root of a model prefab.
251
+ /// Returns the prefab parent of go if it's the root of a model prefab.
252
+ /// Returns null in all other circumstances.
217
253
/// </summary>
218
- /// <returns><c>true</c> if go is a model instance; otherwise, <c>false</c> .</returns>
254
+ /// <returns>The root of a model prefab asset, or null .</returns>
219
255
/// <param name="go">Go.</param>
220
- public static bool IsModelInstance ( GameObject go ) {
256
+ public static GameObject GetModelPrefabOrNull ( GameObject go ) {
221
257
// Children of model prefab instances will also have "model prefab instance"
222
258
// as their prefab type, so it is important that it is the root that is selected.
223
259
//
@@ -229,7 +265,22 @@ public static bool IsModelInstance(GameObject go){
229
265
// However, when selecting the Sphere to convert, we don't want to connect it to the
230
266
// existing FBX but create a new FBX containing just the sphere.
231
267
PrefabType unityPrefabType = PrefabUtility . GetPrefabType ( go ) ;
232
- return unityPrefabType == PrefabType . ModelPrefabInstance && go . Equals ( PrefabUtility . FindPrefabRoot ( go ) ) ;
268
+ switch ( unityPrefabType ) {
269
+ case PrefabType . ModelPrefabInstance :
270
+ if ( go . Equals ( PrefabUtility . FindPrefabRoot ( go ) ) ) {
271
+ return PrefabUtility . GetPrefabParent ( go ) as GameObject ;
272
+ } else {
273
+ return null ;
274
+ }
275
+ case PrefabType . ModelPrefab :
276
+ if ( go . Equals ( PrefabUtility . FindPrefabRoot ( go ) ) ) {
277
+ return go ;
278
+ } else {
279
+ return null ;
280
+ }
281
+ default :
282
+ return null ;
283
+ }
233
284
}
234
285
235
286
/// <summary>
0 commit comments