Skip to content

Commit 36ac243

Browse files
authored
Merge pull request #54 from Unity-Technologies/UNI-21707-improve-export-performance
UNI-21707 improve export performance
2 parents e4b8f87 + 424921e commit 36ac243

File tree

4 files changed

+244
-93
lines changed

4 files changed

+244
-93
lines changed

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -978,7 +978,7 @@ static void OnContextItem (MenuCommand command)
978978
///<summary>
979979
///Information about the mesh that is important for exporting.
980980
///</summary>
981-
public struct MeshInfo
981+
public class MeshInfo
982982
{
983983
/// <summary>
984984
/// The transform of the mesh.
@@ -1009,19 +1009,33 @@ public struct MeshInfo
10091009
/// Ex: if triangles = [3,4,2], then we have one triangle with vertices vertices[3], vertices[4], and vertices[2]
10101010
/// </summary>
10111011
/// <value>The triangles.</value>
1012-
public int [] Triangles { get { return mesh.triangles; } }
1012+
private int[] m_triangles;
1013+
public int [] Triangles { get {
1014+
if(m_triangles == null) { m_triangles = mesh.triangles; }
1015+
return m_triangles;
1016+
} }
10131017

10141018
/// <summary>
10151019
/// Gets the vertices, represented in local coordinates.
10161020
/// </summary>
10171021
/// <value>The vertices.</value>
1018-
public Vector3 [] Vertices { get { return mesh.vertices; } }
1022+
private Vector3[] m_vertices;
1023+
public Vector3 [] Vertices { get {
1024+
if(m_vertices == null) { m_vertices = mesh.vertices; }
1025+
return m_vertices;
1026+
} }
10191027

10201028
/// <summary>
10211029
/// Gets the normals for the vertices.
10221030
/// </summary>
10231031
/// <value>The normals.</value>
1024-
public Vector3 [] Normals { get { return mesh.normals; } }
1032+
private Vector3[] m_normals;
1033+
public Vector3 [] Normals { get {
1034+
if (m_normals == null) {
1035+
m_normals = mesh.normals;
1036+
}
1037+
return m_normals;
1038+
} }
10251039

10261040
/// <summary>
10271041
/// TODO: Gets the binormals for the vertices.
@@ -1036,12 +1050,15 @@ public Vector3 [] Binormals {
10361050
/// => Math.cross (normal, tangent.xyz) * tangent.w
10371051
if (m_Binormals == null || m_Binormals.Length == 0)
10381052
{
1039-
m_Binormals = new Vector3 [mesh.normals.Length];
1053+
var normals = Normals;
1054+
var tangents = Tangents;
1055+
1056+
m_Binormals = new Vector3 [normals.Length];
10401057

1041-
for (int i = 0; i < mesh.normals.Length; i++)
1042-
m_Binormals [i] = Vector3.Cross (mesh.normals [i],
1043-
mesh.tangents [i])
1044-
* mesh.tangents [i].w;
1058+
for (int i = 0; i < normals.Length; i++)
1059+
m_Binormals [i] = Vector3.Cross (normals [i],
1060+
tangents [i])
1061+
* tangents [i].w;
10451062

10461063
}
10471064
return m_Binormals;
@@ -1052,19 +1069,37 @@ public Vector3 [] Binormals {
10521069
/// TODO: Gets the tangents for the vertices.
10531070
/// </summary>
10541071
/// <value>The tangents.</value>
1055-
public Vector4 [] Tangents { get { return mesh.tangents; } }
1072+
private Vector4[] m_tangents;
1073+
public Vector4 [] Tangents { get {
1074+
if (m_tangents == null) {
1075+
m_tangents = mesh.tangents;
1076+
}
1077+
return m_tangents;
1078+
} }
10561079

10571080
/// <summary>
1058-
/// TODO: Gets the tangents for the vertices.
1081+
/// TODO: Gets the vertex colors for the vertices.
10591082
/// </summary>
1060-
/// <value>The tangents.</value>
1061-
public Color32 [] VertexColors { get { return mesh.colors32; } }
1083+
/// <value>The vertex colors.</value>
1084+
private Color32 [] m_vertexColors;
1085+
public Color32 [] VertexColors { get {
1086+
if (m_vertexColors == null) {
1087+
m_vertexColors = mesh.colors32;
1088+
}
1089+
return m_vertexColors;
1090+
} }
10621091

10631092
/// <summary>
10641093
/// Gets the uvs.
10651094
/// </summary>
10661095
/// <value>The uv.</value>
1067-
public Vector2 [] UV { get { return mesh.uv; } }
1096+
private Vector2[] m_UVs;
1097+
public Vector2 [] UV { get {
1098+
if (m_UVs == null) {
1099+
m_UVs = mesh.uv;
1100+
}
1101+
return m_UVs;
1102+
} }
10681103

10691104
/// <summary>
10701105
/// The material used, if any; otherwise null.
@@ -1104,6 +1139,12 @@ public MeshInfo (Mesh mesh)
11041139
this.xform = Matrix4x4.identity;
11051140
this.unityObject = null;
11061141
this.m_Binormals = null;
1142+
this.m_vertices = null;
1143+
this.m_triangles = null;
1144+
this.m_normals = null;
1145+
this.m_UVs = null;
1146+
this.m_vertexColors = null;
1147+
this.m_tangents = null;
11071148
}
11081149

11091150
/// <summary>
@@ -1117,6 +1158,12 @@ public MeshInfo (GameObject gameObject, Mesh mesh)
11171158
this.xform = gameObject.transform.localToWorldMatrix;
11181159
this.unityObject = gameObject;
11191160
this.m_Binormals = null;
1161+
this.m_vertices = null;
1162+
this.m_triangles = null;
1163+
this.m_normals = null;
1164+
this.m_UVs = null;
1165+
this.m_vertexColors = null;
1166+
this.m_tangents = null;
11201167
}
11211168
}
11221169

@@ -1158,7 +1205,7 @@ private MeshInfo GetMeshInfo (GameObject gameObject, bool requireRenderer = true
11581205
}
11591206
}
11601207
if (!mesh) {
1161-
return new MeshInfo ();
1208+
return new MeshInfo(null);
11621209
}
11631210
return new MeshInfo (gameObject, mesh);
11641211
}

Assets/FbxExporters/Editor/UnitTests/DefaultSelectionTest.cs

Lines changed: 4 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -13,62 +13,14 @@ namespace FbxExporters.UnitTests
1313
/// Tests that the right GameObjects are exported and
1414
/// that they have the expected transforms.
1515
/// </summary>
16-
public class DefaultSelectionTest
16+
public class DefaultSelectionTest : ExporterTestBase
1717
{
18-
private string _filePath;
19-
protected string filePath { get { return string.IsNullOrEmpty(_filePath) ? Application.dataPath : _filePath; } set { _filePath = value; } }
20-
21-
private string _fileNamePrefix;
22-
protected string fileNamePrefix { get { return string.IsNullOrEmpty(_fileNamePrefix) ? "_safe_to_delete__" : _fileNamePrefix; }
23-
set { _fileNamePrefix = value; } }
24-
25-
private string _fileNameExt;
26-
protected string fileNameExt { get { return string.IsNullOrEmpty(_fileNameExt) ? ".fbx" : _fileNameExt; } set { _fileNameExt = value; } }
27-
28-
private string MakeFileName(string baseName = null, string prefixName = null, string extName = null)
29-
{
30-
if (baseName==null)
31-
baseName = Path.GetRandomFileName();
32-
33-
if (prefixName==null)
34-
prefixName = this.fileNamePrefix;
35-
36-
if (extName==null)
37-
extName = this.fileNameExt;
38-
39-
return prefixName + baseName + extName;
40-
}
41-
42-
protected string GetRandomFileNamePath(string pathName = null, string prefixName = null, string extName = null)
43-
{
44-
string temp;
45-
46-
if (pathName==null)
47-
pathName = this.filePath;
48-
49-
if (prefixName==null)
50-
prefixName = this.fileNamePrefix;
51-
52-
if (extName==null)
53-
extName = this.fileNameExt;
54-
55-
// repeat until you find a file that does not already exist
56-
do {
57-
temp = Path.Combine (pathName, MakeFileName(prefixName: prefixName, extName: extName));
58-
59-
} while(File.Exists (temp));
60-
61-
return temp;
62-
}
63-
6418
protected GameObject m_root;
6519

6620
[TearDown]
67-
public void Term ()
21+
public override void Term ()
6822
{
69-
foreach (string file in Directory.GetFiles (this.filePath, MakeFileName("*"))) {
70-
File.Delete (file);
71-
}
23+
base.Term ();
7224
if (m_root) {
7325
UnityEngine.Object.DestroyImmediate (m_root);
7426
}
@@ -212,7 +164,7 @@ private GameObject CreateGameObject(string name, Transform parent = null)
212164
var go = new GameObject (name);
213165
go.transform.SetParent (parent);
214166
return go;
215-
}
167+
}
216168

217169
private void CompareHierarchies(
218170
GameObject expectedHierarchy, GameObject actualHierarchy,
@@ -256,31 +208,5 @@ private void CompareHierarchies(GameObject[] expectedHierarchy, GameObject[] act
256208
CompareGlobalTransform (actualHierarchy [i].transform, expectedHierarchy [i].transform);
257209
}
258210
}
259-
260-
private GameObject ExportSelection(Object[] selected)
261-
{
262-
// export selected to a file, then return the root
263-
var filename = GetRandomFileNamePath();
264-
265-
Debug.unityLogger.logEnabled = false;
266-
var fbxFileName = FbxExporters.Editor.ModelExporter.ExportObjects (filename, selected) as string;
267-
Debug.unityLogger.logEnabled = true;
268-
269-
Assert.IsNotNull (fbxFileName);
270-
271-
// make filepath relative to project folder
272-
if (fbxFileName.StartsWith (Application.dataPath, System.StringComparison.CurrentCulture))
273-
{
274-
fbxFileName = "Assets" + fbxFileName.Substring (Application.dataPath.Length);
275-
}
276-
// refresh the assetdata base so that we can query for the model
277-
AssetDatabase.Refresh ();
278-
279-
Object unityMainAsset = AssetDatabase.LoadMainAssetAtPath (fbxFileName);
280-
var fbxRoot = unityMainAsset as GameObject;
281-
282-
Assert.IsNotNull (fbxRoot);
283-
return fbxRoot;
284-
}
285211
}
286212
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using UnityEngine;
2+
using UnityEditor;
3+
using UnityEngine.TestTools;
4+
using NUnit.Framework;
5+
using System.Collections;
6+
using System.Diagnostics;
7+
8+
namespace FbxExporters.UnitTests
9+
{
10+
public class ExportPerformanceTest : ExporterTestBase
11+
{
12+
13+
private const int NumMeshesToCombine = 15;
14+
15+
// Export should not take longer than this, otherwise test fails
16+
private const long PerformanceThresholdMilliseconds = 30000;
17+
private Stopwatch m_stopwatch;
18+
private GameObject m_toExport;
19+
20+
[SetUp]
21+
public void Init()
22+
{
23+
m_stopwatch = new Stopwatch ();
24+
m_toExport = CreateGameObjectToExport ();
25+
}
26+
27+
[TearDown]
28+
public override void Term()
29+
{
30+
base.Term ();
31+
GameObject.DestroyImmediate (m_toExport);
32+
}
33+
34+
35+
/// <summary>
36+
/// Creates a GameObject containing a very large mesh to export.
37+
/// </summary>
38+
/// <returns>The game object to export.</returns>
39+
private GameObject CreateGameObjectToExport ()
40+
{
41+
CombineInstance[] combine = new CombineInstance[NumMeshesToCombine];
42+
GameObject spheres = GameObject.CreatePrimitive (PrimitiveType.Sphere);
43+
44+
Transform sphereTransform = spheres.transform;
45+
MeshFilter sphereMeshFilter = spheres.GetComponent<MeshFilter> ();
46+
Assert.IsNotNull (sphereMeshFilter);
47+
48+
for (int i = 0; i < NumMeshesToCombine; i++) {
49+
combine [i].mesh = sphereMeshFilter.sharedMesh;
50+
sphereTransform.position = new Vector3 (i, i, i);
51+
combine [i].transform = sphereTransform.localToWorldMatrix;
52+
}
53+
54+
sphereMeshFilter.sharedMesh = new Mesh ();
55+
sphereMeshFilter.sharedMesh.name = "Spheres Mesh";
56+
sphereMeshFilter.sharedMesh.CombineMeshes (combine);
57+
return spheres;
58+
}
59+
60+
[Test]
61+
public void TestPerformance ()
62+
{
63+
Assert.IsNotNull (m_toExport);
64+
65+
var filename = GetRandomFileNamePath();
66+
67+
UnityEngine.Debug.unityLogger.logEnabled = false;
68+
69+
m_stopwatch.Reset ();
70+
m_stopwatch.Start ();
71+
var fbxFileName = FbxExporters.Editor.ModelExporter.ExportObjects (filename, new Object[]{m_toExport}) as string;
72+
m_stopwatch.Stop ();
73+
74+
UnityEngine.Debug.unityLogger.logEnabled = true;
75+
76+
Assert.IsNotNull (fbxFileName);
77+
Assert.LessOrEqual(m_stopwatch.ElapsedMilliseconds, PerformanceThresholdMilliseconds);
78+
}
79+
}
80+
}

0 commit comments

Comments
 (0)