Skip to content

Commit 827911c

Browse files
author
Benoit Hudson
committed
New CreateInstantiatedModelPrefab version to convert one object at a time.
Simpler API; useful for tests I'm about to commit. Updated the FbxPrefabTest to stop ignoring the result (which I fixed a few commits ago).
1 parent fa9e96e commit 827911c

File tree

2 files changed

+110
-89
lines changed

2 files changed

+110
-89
lines changed

Assets/FbxExporters/Editor/ConvertToModel.cs

Lines changed: 108 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,10 @@ namespace FbxExporters
1616
{
1717
namespace Editor
1818
{
19-
public class ConvertToModel : System.IDisposable
19+
public static class ConvertToModel
2020
{
2121
const string MenuItemName1 = "GameObject/Convert To Prefab";
2222

23-
/// <summary>
24-
/// Clean up this class on garbage collection
25-
/// </summary>
26-
public void Dispose () { }
27-
2823
// Add a menu item called "Export Model..." to a GameObject's context menu.
2924
// OnContextItem gets called once per selected object
3025
// (if the parent and child are selected, then OnContextItem will only be called on the parent)
@@ -66,113 +61,140 @@ public static bool OnValidateMenuItem ()
6661
}
6762

6863
/// <summary>
69-
/// Create an instantiated model prefab from an game object hierarchy.
64+
/// Create instantiated model prefabs from a selection of objects.
65+
///
66+
/// Every hierarchy in the selection will be exported, under the name of the root.
67+
///
68+
/// If an object and one of its descendents are both selected, the descendent is not promoted to be a prefab -- we only export the root.
7069
/// </summary>
7170
/// <returns>list of instanced Model Prefabs</returns>
7271
/// <param name="unityGameObjectsToConvert">Unity game objects to convert to Model Prefab instances</param>
7372
/// <param name="path">Path to save Model Prefab; use FbxExportSettings if null</param>
7473
/// <param name="keepOriginal">If set to <c>true</c> keep original gameobject hierarchy.</param>
75-
public static GameObject[] CreateInstantiatedModelPrefab (GameObject [] unityGameObjectsToConvert, string path = null, bool keepOriginal = true)
74+
public static GameObject[] CreateInstantiatedModelPrefab (GameObject [] unityGameObjectsToConvert, string directoryFullPath = null, bool keepOriginal = true)
7675
{
77-
if (path == null) {
78-
path = FbxExporters.EditorTools.ExportSettings.GetAbsoluteSavePath();
76+
if (directoryFullPath == null) {
77+
directoryFullPath = FbxExporters.EditorTools.ExportSettings.GetAbsoluteSavePath();
7978
} else {
80-
path = Path.GetFullPath(path);
79+
directoryFullPath = Path.GetFullPath(directoryFullPath);
8180
}
8281

83-
List<GameObject> result = new List<GameObject> ();
84-
85-
var exportSet = ModelExporter.RemoveRedundantObjects (unityGameObjectsToConvert);
86-
GameObject[] gosToExport = new GameObject[exportSet.Count];
87-
exportSet.CopyTo (gosToExport);
88-
89-
EnforceUniqueNames (gosToExport);
90-
91-
// find common ancestor root & filePath;
92-
string[] filePaths = new string[gosToExport.Length];
93-
94-
for(int n = 0; n < gosToExport.Length; n++){
95-
var filename = ModelExporter.ConvertToValidFilename (gosToExport [n].name + ".fbx");
96-
var filePath = Path.Combine (path, filename);
97-
if (File.Exists (filePath)) {
98-
filePath = IncrementFileName (path, filename);
82+
var toExport = ModelExporter.RemoveRedundantObjects (unityGameObjectsToConvert);
83+
var wasExported = new List<GameObject>();
84+
foreach(var go in toExport) {
85+
try {
86+
wasExported.Add(CreateInstantiatedModelPrefab(go,
87+
directoryFullPath: directoryFullPath,
88+
keepOriginal: keepOriginal));
89+
} catch(System.Exception xcp) {
90+
Debug.LogException(xcp);
9991
}
100-
filePaths[n] = filePath;
10192
}
93+
return wasExported.ToArray();
94+
}
10295

103-
string[] fbxFileNames = new string[filePaths.Length];
96+
/// <summary>
97+
/// Convert one object (and the hierarchy below it) to an auto-updating prefab.
98+
///
99+
/// This returns a new object; the converted object may be modified or destroyed.
100+
///
101+
/// This refreshes the asset database.
102+
///
103+
/// If "keepOriginal" is set, the converted object is modified but remains in the scene.
104+
/// </summary>
105+
/// <returns>The instance that replaces 'toConvert' in the scene.</returns>
106+
/// <param name="toConvert">GameObject hierarchy to replace with a prefab.</param>
107+
/// <param name="fbxFullPath">Absolute platform-specific path to the fbx file. May be null, in which case we construct a path from the object name and the directoryFullPath.</param>
108+
/// <param name="directoryFullPath">Absolute platform-specific path to a directory in which to put the fbx file. Ignored if fbxFullPath is specified. May be null, in which case we use the export settings.</param>
109+
/// <param name="keepOriginal">If set to <c>true</c>, keep the original in the scene.</param>
110+
public static GameObject CreateInstantiatedModelPrefab (
111+
GameObject toConvert,
112+
string directoryFullPath = null,
113+
string fbxFullPath = null,
114+
bool keepOriginal = true)
115+
{
116+
// Generate a unique filename.
117+
if (string.IsNullOrEmpty(fbxFullPath)) {
118+
if (string.IsNullOrEmpty (directoryFullPath)) {
119+
directoryFullPath = FbxExporters.EditorTools.ExportSettings.GetAbsoluteSavePath ();
120+
} else {
121+
directoryFullPath = Path.GetFullPath (directoryFullPath);
122+
}
123+
var fbxBasename = ModelExporter.ConvertToValidFilename (toConvert.name + ".fbx");
104124

105-
for (int j = 0; j < gosToExport.Length; j++) {
106-
fbxFileNames[j] = FbxExporters.Editor.ModelExporter.ExportObjects (filePaths[j],
107-
new UnityEngine.Object[] {gosToExport[j]}) as string;
125+
fbxFullPath = Path.Combine (directoryFullPath, fbxBasename);
126+
if (File.Exists (fbxFullPath)) {
127+
fbxFullPath = IncrementFileName (directoryFullPath, fbxFullPath);
128+
}
108129
}
130+
var assetRelativePath = FbxExporters.EditorTools.ExportSettings.ConvertToAssetRelativePath(fbxFullPath);
131+
var projectRelativePath = "Assets/" + assetRelativePath;
132+
if (string.IsNullOrEmpty(assetRelativePath)) {
133+
throw new System.Exception("Path " + fbxFullPath + " must be in the Assets folder.");
134+
}
135+
136+
// Make sure that the object names in the hierarchy are unique.
137+
// The import back in to Unity would do this automatically but we prefer to control it so that the Maya artist can see the same names as exist in Unity.
138+
EnforceUniqueNames (new GameObject[] {toConvert});
109139

110-
for(int i = 0; i < fbxFileNames.Length; i++)
140+
// Export to FBX. It refreshes the database.
111141
{
112-
var fbxFileName = fbxFileNames [i];
113-
if (fbxFileName == null) {
114-
Debug.LogWarning (string.Format ("Warning: Export failed for GameObject {0}", gosToExport [i].name));
115-
continue;
142+
var fbxActualPath = ModelExporter.ExportObject (fbxFullPath, toConvert);
143+
if (fbxActualPath != fbxFullPath) {
144+
throw new System.Exception ("Failed to convert " + toConvert.name);
116145
}
146+
}
117147

118-
// make filepath relative
119-
var assetRelativePath = FbxExporters.EditorTools.ExportSettings.ConvertToAssetRelativePath(fbxFileName);
120-
var projectRelativePath = "Assets/" + assetRelativePath;
121-
122-
// refresh the assetdata base so that we can query for the model
123-
AssetDatabase.Refresh ();
124-
125-
// Replace w Model asset. LoadMainAssetAtPath wants a path
126-
// relative to the project, not relative to the assets
127-
// folder.
128-
var unityMainAsset = AssetDatabase.LoadMainAssetAtPath(projectRelativePath) as GameObject;
148+
// Munge the path: we'll be using APIs that need a path relative to the assets folder.
129149

130-
if (!unityMainAsset) {
131-
continue;
132-
}
133150

134-
// Instantiate the FBX file.
135-
Object unityObj = PrefabUtility.InstantiatePrefab (unityMainAsset, gosToExport[i].scene);
136-
GameObject unityGO = unityObj as GameObject;
137-
if (!unityGO) {
138-
continue;
139-
}
151+
// Replace w Model asset. LoadMainAssetAtPath wants a path
152+
// relative to the project, not relative to the assets
153+
// folder.
154+
Debug.Log(projectRelativePath);
155+
var unityMainAsset = AssetDatabase.LoadMainAssetAtPath (projectRelativePath) as GameObject;
156+
if (!unityMainAsset) {
157+
throw new System.Exception ("Failed to convert " + toConvert.name);;
158+
}
140159

141-
// Copy the components over to the instance of the FBX.
142-
SetupImportedGameObject (gosToExport [i], unityGO);
160+
// Instantiate the FBX file.
161+
var unityGO = PrefabUtility.InstantiatePrefab (unityMainAsset, toConvert.scene)
162+
as GameObject;
163+
if (!unityGO) {
164+
throw new System.Exception ("Failed to convert " + toConvert.name);;
165+
}
143166

144-
// Set up the FbxPrefab component so we can auto-update.
145-
var fbxPrefab = unityGO.AddComponent<FbxPrefab>();
146-
fbxPrefab.SetSourceModel(unityMainAsset);
167+
// Copy the components over to the instance of the FBX.
168+
SetupImportedGameObject (toConvert, unityGO);
147169

148-
// Disconnect from the FBX file.
149-
PrefabUtility.DisconnectPrefabInstance(unityGO);
170+
// Set up the FbxPrefab component so it will auto-update.
171+
var fbxPrefab = unityGO.AddComponent<FbxPrefab>();
172+
fbxPrefab.SetSourceModel(unityMainAsset);
150173

151-
// Create a prefab from the instantiated and componentized unityGO.
152-
var prefabFileName = Path.ChangeExtension(projectRelativePath, ".prefab");
153-
var prefab = PrefabUtility.CreatePrefab(prefabFileName, unityGO);
154-
if (!prefab) {
155-
throw new System.Exception(
156-
string.Format("Failed to create prefab asset in [{0}] from fbx [{1}]",
157-
prefabFileName, fbxFileName));
158-
}
159-
// Connect to the prefab file.
160-
unityGO = PrefabUtility.ConnectGameObjectToPrefab(unityGO, prefab);
174+
// Disconnect from the FBX file.
175+
PrefabUtility.DisconnectPrefabInstance(unityGO);
161176

162-
// Remove (now redundant) gameobject
163-
if (!keepOriginal) {
164-
Object.DestroyImmediate (unityGameObjectsToConvert [i]);
165-
} else {
166-
// rename and put under scene root in case we need to check values
167-
gosToExport [i].name = "_safe_to_delete_" + gosToExport [i].name;
168-
gosToExport [i].SetActive (false);
169-
}
177+
// Create a prefab from the instantiated and componentized unityGO.
178+
var prefabFileName = Path.ChangeExtension(projectRelativePath, ".prefab");
179+
var prefab = PrefabUtility.CreatePrefab(prefabFileName, unityGO);
180+
if (!prefab) {
181+
throw new System.Exception(
182+
string.Format("Failed to create prefab asset in [{0}] from fbx [{1}]",
183+
prefabFileName, fbxFullPath));
184+
}
185+
// Connect to the prefab file.
186+
unityGO = PrefabUtility.ConnectGameObjectToPrefab(unityGO, prefab);
170187

171-
// add the instanced prefab
172-
result.Add (unityGO);
188+
// Remove (now redundant) gameobject
189+
if (!keepOriginal) {
190+
Object.DestroyImmediate (toConvert);
191+
} else {
192+
// rename and put under scene root in case we need to check values
193+
toConvert.name = "_safe_to_delete_" + toConvert.name;
194+
toConvert.SetActive (false);
173195
}
174196

175-
return result.ToArray ();
197+
return unityGO;
176198
}
177199

178200
/// <summary>
@@ -217,7 +239,7 @@ public static string IncrementFileName(string path, string filename)
217239
/// e.g. Sphere becomes Sphere 1
218240
/// </summary>
219241
/// <param name="exportSet">Export set.</param>
220-
public static void EnforceUniqueNames(GameObject[] exportSet)
242+
public static void EnforceUniqueNames(IEnumerable<GameObject> exportSet)
221243
{
222244
Dictionary<string, int> NameToIndexMap = new Dictionary<string, int> ();
223245
string format = "{0} {1}";

Assets/FbxExporters/Editor/UnitTests/FbxPrefabTest.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ public void ManualToAuto() {
337337

338338
public class FbxPrefabRegressions : ExporterTestBase
339339
{
340-
[Ignore("ConvertToModel return value is messed up.")]
341340
[Test]
342341
public void TestCubeAtRoot()
343342
{
@@ -352,8 +351,8 @@ public void TestCubeAtRoot()
352351
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
353352
cube.name = "Cube";
354353
var cubeAssetPath = GetRandomFbxFilePath();
355-
var autoPrefab = FbxExporters.Editor.ConvertToModel.CreateInstantiatedModelPrefab(
356-
new GameObject[] { cube }, path: cubeAssetPath)[0];
354+
var autoPrefab = FbxExporters.Editor.ConvertToModel.CreateInstantiatedModelPrefab(cube,
355+
fbxFullPath: cubeAssetPath);
357356
Assert.IsTrue(autoPrefab);
358357

359358
// Make a maya locator.

0 commit comments

Comments
 (0)