Skip to content

Commit 64f5e8b

Browse files
author
Benoit Hudson
committed
uni-40523: backend code to support exporting from asset menu
1 parent aae42c4 commit 64f5e8b

File tree

2 files changed

+85
-33
lines changed

2 files changed

+85
-33
lines changed

Assets/FbxExporters/Editor/ConvertToModel.cs

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ namespace Editor
1212
{
1313
public static class ConvertToModel
1414
{
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...";
1617

1718
/// <summary>
1819
/// OnContextItem is called either:
@@ -23,14 +24,22 @@ public static class ConvertToModel
2324
/// parent and child are selected, then OnContextItem will only be
2425
/// called on the parent)
2526
/// </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)
2837
{
2938
GameObject [] selection = null;
3039

3140
if (command == null || command.context == null) {
3241
// 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);
3443
} else {
3544
// We were invoked from the right-click menu, so use the context of the context menu.
3645
var selected = command.context as GameObject;
@@ -48,9 +57,10 @@ static void OnContextItem (MenuCommand command)
4857
}
4958

5059
/// <summary>
51-
// Validate the menu item defined by the function above.
60+
// Validate the menu items defined above.
5261
/// </summary>
53-
[MenuItem (MenuItemName1, true, 30)]
62+
[MenuItem (GameObjectMenuItemName, true, 30)]
63+
[MenuItem (AssetsMenuItemName, true, 30)]
5464
public static bool OnValidateMenuItem ()
5565
{
5666
return true;
@@ -87,8 +97,11 @@ public static GameObject[] CreateInstantiatedModelPrefab (
8797
/// This returns a new object; the converted object may be modified or destroyed.
8898
///
8999
/// 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.
90103
/// </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>
92105
/// <param name="toConvert">GameObject hierarchy to replace with a prefab.</param>
93106
/// <param name="fbxFullPath">Absolute platform-specific path to
94107
/// the fbx file. May be null, in which case we construct a unique
@@ -107,18 +120,15 @@ public static GameObject Convert (
107120
EditorTools.IExportOptions exportOptions = null
108121
)
109122
{
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) {
115126
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);
120128
}
121129

130+
// Otherwise, do export.
131+
122132
if (string.IsNullOrEmpty(fbxFullPath)) {
123133
// Generate a unique filename.
124134
if (string.IsNullOrEmpty (fbxDirectoryFullPath)) {
@@ -178,20 +188,39 @@ public static GameObject Convert (
178188
}
179189
var prefabProjectRelativePath = EditorTools.ExportSettings.GetProjectRelativePath (prefabFullPath);
180190

181-
SetupFbxPrefab (toConvert, unityMainAsset, prefabProjectRelativePath, fbxFullPath);
182-
191+
// Create the FbxPrefab component and the prefab asset itself.
192+
toConvert = SetupFbxPrefab (toConvert, unityMainAsset, prefabProjectRelativePath);
183193
toConvert.name = Path.GetFileNameWithoutExtension (fbxFullPath);
194+
184195
return toConvert;
185196
}
186197

187198
/// <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.
189203
/// </summary>
190204
/// <param name="toConvert">Hierarchy to convert.</param>
191205
/// <param name="unityMainAsset">Main asset in the FBX.</param>
192206
/// <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+
195224
// Set up the FbxPrefab component so it will auto-update.
196225
// Make sure to delete whatever FbxPrefab history we had.
197226
var fbxPrefab = toConvert.GetComponent<FbxPrefab>();
@@ -206,18 +235,25 @@ public static void SetupFbxPrefab(GameObject toConvert, GameObject unityMainAsse
206235
var prefabFileName = Path.ChangeExtension(projectRelativePath, ".prefab");
207236
var prefab = PrefabUtility.CreatePrefab(prefabFileName, toConvert, ReplacePrefabOptions.ConnectToPrefab);
208237
if (!prefab) {
238+
var fbxFullPath = AssetDatabase.GetAssetPath(unityMainAsset);
209239
throw new System.Exception(
210240
string.Format("Failed to create prefab asset in [{0}] from fbx [{1}]",
211241
prefabFileName, fbxFullPath));
212242
}
243+
244+
return toConvert;
213245
}
214246

215247
/// <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.
217253
/// </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>
219255
/// <param name="go">Go.</param>
220-
public static bool IsModelInstance(GameObject go){
256+
public static GameObject GetModelPrefabOrNull(GameObject go){
221257
// Children of model prefab instances will also have "model prefab instance"
222258
// as their prefab type, so it is important that it is the root that is selected.
223259
//
@@ -229,7 +265,22 @@ public static bool IsModelInstance(GameObject go){
229265
// However, when selecting the Sphere to convert, we don't want to connect it to the
230266
// existing FBX but create a new FBX containing just the sphere.
231267
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+
}
233284
}
234285

235286
/// <summary>

Assets/FbxExporters/Editor/ConvertToPrefabEditorWindow.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,19 @@ protected void SetGameObjectsToConvert(IEnumerable<GameObject> toConvert){
4848
if (ToExport.Length == 1) {
4949
var go = ModelExporter.GetGameObject (ToExport [0]);
5050
// check if the GameObject is a model instance, use as default filename and path if it is
51-
if(ConvertToModel.IsModelInstance(go)) {
52-
var mainAsset = PrefabUtility.GetPrefabParent (go) as GameObject;
51+
var mainAsset = ConvertToModel.GetModelPrefabOrNull(go);
52+
if (!mainAsset) {
53+
// Use the game object's name
54+
m_prefabFileName = go.name;
55+
} else {
56+
// Use the asset's name
5357
var mainAssetRelPath = AssetDatabase.GetAssetPath (mainAsset);
5458
// remove Assets/ from beginning of path
5559
mainAssetRelPath = mainAssetRelPath.Substring ("Assets".Length);
5660

5761
m_prefabFileName = System.IO.Path.GetFileNameWithoutExtension (mainAssetRelPath);
5862
ExportSettings.AddFbxSavePath (System.IO.Path.GetDirectoryName (mainAssetRelPath));
5963
}
60-
else{
61-
m_prefabFileName = ToExport [0].name;
62-
}
6364

6465
// if only one object selected, set transfer source/dest to this object
6566
if (go) {
@@ -113,16 +114,16 @@ protected override bool Export ()
113114
}
114115

115116
// Only create the prefab (no FBX export) if we have selected the root of a model prefab instance.
116-
if(ConvertToModel.IsModelInstance(go)) {
117+
var mainAsset = ConvertToModel.GetModelPrefabOrNull(go);
118+
if (mainAsset) {
117119
// don't re-export fbx
118120
// create prefab out of model instance in scene, link to existing fbx
119-
var mainAsset = PrefabUtility.GetPrefabParent(go) as GameObject;
120121
var mainAssetRelPath = AssetDatabase.GetAssetPath(mainAsset);
121122
var mainAssetAbsPath = System.IO.Directory.GetParent(Application.dataPath) + "/" + mainAssetRelPath;
122123
var relPrefabPath = ExportSettings.GetProjectRelativePath (prefabPath);
123124

124125
if (string.Equals(System.IO.Path.GetFullPath(fbxPath), System.IO.Path.GetFullPath(mainAssetAbsPath))) {
125-
ConvertToModel.SetupFbxPrefab(go, mainAsset, relPrefabPath, mainAssetAbsPath);
126+
ConvertToModel.SetupFbxPrefab(go, mainAsset, relPrefabPath);
126127
return true;
127128
}
128129
}

0 commit comments

Comments
 (0)