Skip to content

Commit ab5c025

Browse files
authored
Merge pull request #313 from Unity-Technologies/UNI-39477-select-LOD-to-export
Uni 39477 select lod to export
2 parents b9355a0 + 6f717f4 commit ab5c025

File tree

5 files changed

+180
-12
lines changed

5 files changed

+180
-12
lines changed

Assets/FbxExporters/Editor/ConvertToModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ public static GameObject Convert (
168168

169169
// Export to FBX. It refreshes the database.
170170
{
171-
var fbxActualPath = ModelExporter.ExportObject (fbxFullPath, toConvert);
171+
var fbxActualPath = ModelExporter.ExportObject (fbxFullPath, toConvert, lodExportType: EditorTools.ExportSettings.LODExportType.All);
172172
if (fbxActualPath != fbxFullPath) {
173173
throw new System.Exception ("Failed to convert " + toConvert.name);
174174
}

Assets/FbxExporters/Editor/FbxExportSettings.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ public override void OnInspectorGUI() {
6262
exportSettings.ExportFormatSelection = EditorGUILayout.Popup(exportSettings.ExportFormatSelection, new string[]{"Binary", "ASCII"});
6363
GUILayout.EndHorizontal();
6464

65+
GUILayout.BeginHorizontal();
66+
EditorGUILayout.LabelField(new GUIContent("LOD Export:", "Select which LOD to export."), GUILayout.Width(LabelWidth - FieldOffset));
67+
exportSettings.lodExportType = (ExportSettings.LODExportType)EditorGUILayout.Popup((int)exportSettings.lodExportType, new string[]{"All", "Highest", "Lowest"});
68+
GUILayout.EndHorizontal();
69+
6570
GUILayout.BeginHorizontal();
6671
EditorGUILayout.LabelField(new GUIContent(
6772
"Export Path:",
@@ -442,6 +447,15 @@ public static string[] DCCVendorLocations
442447

443448
public int selectedDCCApp = 0;
444449

450+
[SerializeField]
451+
public LODExportType lodExportType = LODExportType.All;
452+
453+
public enum LODExportType {
454+
All = 0,
455+
Highest = 1,
456+
Lowest = 2
457+
}
458+
445459
/// <summary>
446460
/// The path where Convert To Model will save the new fbx and prefab.
447461
///

Assets/FbxExporters/Editor/FbxExporter.cs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using UnityEngine.Playables;
99
using UnityEngine.Timeline;
10+
using FbxExporters.EditorTools;
1011

1112
namespace FbxExporters
1213
{
@@ -1922,7 +1923,9 @@ private string GetUniqueName(string name)
19221923
protected int ExportTransformHierarchy(
19231924
GameObject unityGo, FbxScene fbxScene, FbxNode fbxNodeParent,
19241925
int exportProgress, int objectCount, Vector3 newCenter,
1925-
TransformExportType exportType = TransformExportType.Local)
1926+
TransformExportType exportType = TransformExportType.Local,
1927+
ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All
1928+
)
19261929
{
19271930
int numObjectsExported = exportProgress;
19281931

@@ -1957,9 +1960,45 @@ protected int ExportTransformHierarchy(
19571960

19581961
fbxNodeParent.AddChild (fbxNode);
19591962

1963+
// if this object has an LOD group, then export according to the LOD preference setting
1964+
var lodGroup = unityGo.GetComponent<LODGroup>();
1965+
if (lodGroup && lodExportType != ExportSettings.LODExportType.All) {
1966+
LOD[] lods = lodGroup.GetLODs ();
1967+
1968+
// LODs are ordered from highest to lowest.
1969+
// If exporting lowest LOD, reverse the array
1970+
if (lodExportType == ExportSettings.LODExportType.Lowest) {
1971+
// reverse the array
1972+
LOD[] tempLods = new LOD[lods.Length];
1973+
System.Array.Copy (lods, tempLods, lods.Length);
1974+
System.Array.Reverse (tempLods);
1975+
lods = tempLods;
1976+
}
1977+
1978+
for(int i = 0; i < lods.Length; i++){
1979+
var lod = lods [i];
1980+
bool exportedRenderer = false;
1981+
foreach (var renderer in lod.renderers) {
1982+
// only export if parented under LOD group
1983+
if (renderer.transform.parent == unityGo.transform) {
1984+
numObjectsExported = ExportTransformHierarchy (renderer.gameObject, fbxScene, fbxNode, numObjectsExported, objectCount, newCenter, lodExportType: lodExportType);
1985+
exportedRenderer = true;
1986+
} else if(Verbose) {
1987+
Debug.LogFormat ("FbxExporter: Not exporting LOD {0}: {1}", i, renderer.name);
1988+
}
1989+
}
1990+
1991+
// if at least one renderer for this LOD was exported, then we succeeded
1992+
// so stop exporting.
1993+
if (exportedRenderer) {
1994+
return numObjectsExported;
1995+
}
1996+
}
1997+
}
1998+
19601999
// now unityGo through our children and recurse
19612000
foreach (Transform childT in unityGo.transform) {
1962-
numObjectsExported = ExportTransformHierarchy (childT.gameObject, fbxScene, fbxNode, numObjectsExported, objectCount, newCenter);
2001+
numObjectsExported = ExportTransformHierarchy (childT.gameObject, fbxScene, fbxNode, numObjectsExported, objectCount, newCenter, lodExportType: lodExportType);
19632002
}
19642003

19652004
return numObjectsExported;
@@ -2665,7 +2704,11 @@ public enum TransformExportType { Local, Global, Reset };
26652704
///
26662705
/// This refreshes the asset database.
26672706
/// </summary>
2668-
public int ExportAll (IEnumerable<UnityEngine.Object> unityExportSet, Dictionary<GameObject, AnimationOnlyExportData> animationExportData, TransformExportType exportType = TransformExportType.Global)
2707+
public int ExportAll (
2708+
IEnumerable<UnityEngine.Object> unityExportSet,
2709+
Dictionary<GameObject, AnimationOnlyExportData> animationExportData,
2710+
TransformExportType exportType = TransformExportType.Global,
2711+
ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All)
26692712
{
26702713
exportCancelled = false;
26712714

@@ -2780,7 +2823,7 @@ public int ExportAll (IEnumerable<UnityEngine.Object> unityExportSet, Dictionary
27802823
}
27812824
else {
27822825
exportProgress = this.ExportTransformHierarchy (unityGo, fbxScene, fbxRootNode,
2783-
exportProgress, count, center, exportType);
2826+
exportProgress, count, center, exportType, lodExportType);
27842827
}
27852828
if (exportCancelled || exportProgress < 0) {
27862829
Debug.LogWarning ("Export Cancelled");
@@ -3545,7 +3588,7 @@ private static void OnExport (AnimationExportType exportType = AnimationExportTy
35453588
return;
35463589
}
35473590

3548-
if (ExportObjects (filePath, exportType: exportType) != null) {
3591+
if (ExportObjects (filePath, exportType: exportType, lodExportType: ExportSettings.instance.lodExportType) != null) {
35493592
// refresh the asset database so that the file appears in the
35503593
// asset folder view.
35513594
AssetDatabase.Refresh ();
@@ -3556,7 +3599,12 @@ private static void OnExport (AnimationExportType exportType = AnimationExportTy
35563599
/// Export a list of (Game) objects to FBX file.
35573600
/// Use the SaveFile panel to allow user to enter a file name.
35583601
/// <summary>
3559-
public static string ExportObjects (string filePath, UnityEngine.Object[] objects = null, AnimationExportType exportType = AnimationExportType.all, TransformExportType transformExportType = TransformExportType.Global)
3602+
public static string ExportObjects (
3603+
string filePath,
3604+
UnityEngine.Object[] objects = null,
3605+
AnimationExportType exportType = AnimationExportType.all,
3606+
TransformExportType transformExportType = TransformExportType.Global,
3607+
ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All)
35603608
{
35613609
LastFilePath = filePath;
35623610

@@ -3597,7 +3645,7 @@ public static string ExportObjects (string filePath, UnityEngine.Object[] object
35973645
break;
35983646
}
35993647

3600-
if (fbxExporter.ExportAll (objects, animationExportData, transformExportType) > 0) {
3648+
if (fbxExporter.ExportAll (objects, animationExportData, transformExportType, lodExportType) > 0) {
36013649
string message = string.Format ("Successfully exported: {0}", filePath);
36023650
UnityEngine.Debug.Log (message);
36033651

@@ -3607,9 +3655,13 @@ public static string ExportObjects (string filePath, UnityEngine.Object[] object
36073655
return null;
36083656
}
36093657

3610-
public static string ExportObject (string filePath, UnityEngine.Object root, AnimationExportType exportType = AnimationExportType.all, TransformExportType transformExportType = TransformExportType.Reset)
3658+
public static string ExportObject (
3659+
string filePath, UnityEngine.Object root,
3660+
AnimationExportType exportType = AnimationExportType.all,
3661+
TransformExportType transformExportType = TransformExportType.Reset,
3662+
ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All)
36113663
{
3612-
return ExportObjects(filePath, new Object[] { root }, exportType, transformExportType);
3664+
return ExportObjects(filePath, new Object[] { root }, exportType, transformExportType, lodExportType);
36133665
}
36143666

36153667
private static void EnsureDirectory (string path)

Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,12 @@ protected virtual string ExportSelectedObjects(string filename, params Object[]
261261
/// <returns>The exported fbx file path.</returns>
262262
/// <param name="hierarchy">Hierarchy.</param>
263263
/// <param name="animOnly">If set to <c>true</c> export animation only.</param>
264-
protected string ExportToFbx (GameObject hierarchy, bool animOnly = false){
264+
protected string ExportToFbx (GameObject hierarchy, bool animOnly = false, EditorTools.ExportSettings.LODExportType lodExportType = EditorTools.ExportSettings.LODExportType.All){
265265
string filename = GetRandomFbxFilePath ();
266266
var exportedFilePath = FbxExporters.Editor.ModelExporter.ExportObject (
267-
filename, hierarchy, animOnly? FbxExporters.Editor.ModelExporter.AnimationExportType.componentAnimation : FbxExporters.Editor.ModelExporter.AnimationExportType.all
267+
filename, hierarchy,
268+
animOnly? FbxExporters.Editor.ModelExporter.AnimationExportType.componentAnimation : FbxExporters.Editor.ModelExporter.AnimationExportType.all,
269+
lodExportType: lodExportType
268270
);
269271
Assert.That (exportedFilePath, Is.EqualTo (filename));
270272
return filename;

Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,5 +737,105 @@ public void TestBlendShapeExport(string fbxPath)
737737
}
738738
}
739739
}
740+
741+
[Test]
742+
public void LODExportTest(){
743+
// Create the following test hierarchy:
744+
// LODGroup
745+
// -- Sphere_LOD0
746+
// -- Capsule_LOD0
747+
// -- Cube_LOD2
748+
// Cylinder_LOD1
749+
//
750+
// where sphere + capsule renderers are both in LOD0, and cylinder is in LOD1
751+
// but not parented under the LOD group
752+
753+
var lodGroup = new GameObject ("LODGroup");
754+
var sphereLOD0 = GameObject.CreatePrimitive (PrimitiveType.Sphere);
755+
sphereLOD0.name = "Sphere_LOD0";
756+
var capsuleLOD0 = GameObject.CreatePrimitive (PrimitiveType.Capsule);
757+
capsuleLOD0.name = "Capsule_LOD0";
758+
var cubeLOD2 = GameObject.CreatePrimitive (PrimitiveType.Cube);
759+
cubeLOD2.name = "Cube_LOD2";
760+
var cylinderLOD1 = GameObject.CreatePrimitive (PrimitiveType.Cylinder);
761+
cylinderLOD1.name = "Cylinder_LOD1";
762+
763+
sphereLOD0.transform.SetParent (lodGroup.transform);
764+
capsuleLOD0.transform.SetParent (lodGroup.transform);
765+
cubeLOD2.transform.SetParent (lodGroup.transform);
766+
cylinderLOD1.transform.SetParent (null);
767+
768+
// add LOD group
769+
var lodGroupComp = lodGroup.AddComponent<LODGroup>();
770+
Assert.That (lodGroupComp, Is.Not.Null);
771+
772+
LOD[] lods = new LOD[3];
773+
lods [0] = new LOD (1, new Renderer[]{ sphereLOD0.GetComponent<Renderer>(), capsuleLOD0.GetComponent<Renderer>() });
774+
lods [1] = new LOD (0.75f, new Renderer[] { cylinderLOD1.GetComponent<Renderer>() });
775+
lods [2] = new LOD (0.5f, new Renderer[] { cubeLOD2.GetComponent<Renderer>() });
776+
lodGroupComp.SetLODs (lods);
777+
lodGroupComp.RecalculateBounds ();
778+
779+
// test export all
780+
// expected LODs exported: Sphere_LOD0, Capsule_LOD0, Cube_LOD2
781+
string filename = ExportToFbx(lodGroup, lodExportType:EditorTools.ExportSettings.LODExportType.All);
782+
GameObject fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject;
783+
Assert.IsTrue (fbxObj);
784+
785+
HashSet<string> expectedChildren = new HashSet<string> () { sphereLOD0.name, capsuleLOD0.name, cubeLOD2.name };
786+
CompareGameObjectChildren (fbxObj, expectedChildren);
787+
788+
// test export highest
789+
// expected LODs exported: Sphere_LOD0, Capsule_LOD0
790+
filename = ExportToFbx(lodGroup, lodExportType:EditorTools.ExportSettings.LODExportType.Highest);
791+
fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject;
792+
Assert.IsTrue (fbxObj);
793+
794+
expectedChildren = new HashSet<string> () { sphereLOD0.name, capsuleLOD0.name };
795+
CompareGameObjectChildren (fbxObj, expectedChildren);
796+
797+
// test export lowest
798+
// expected LODs exported: Cube_LOD2
799+
filename = ExportToFbx(lodGroup, lodExportType:EditorTools.ExportSettings.LODExportType.Lowest);
800+
fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject;
801+
Assert.IsTrue (fbxObj);
802+
803+
expectedChildren = new HashSet<string> () { cubeLOD2.name };
804+
CompareGameObjectChildren (fbxObj, expectedChildren);
805+
806+
// test convert to prefab
807+
// this should have the same result as "export all"
808+
// expected LODs exported: Sphere_LOD0, Capsule_LOD0, Cube_LOD2
809+
// NOTE: Cylinder_LOD1 is not exported as it is not under the LODGroup hierarchy being exported
810+
filename = GetRandomFbxFilePath();
811+
var convertedHierarchy = ConvertToModel.Convert(lodGroup, fbxFullPath: filename);
812+
Assert.That (convertedHierarchy, Is.Not.Null);
813+
814+
// check both converted hierarchy and fbx
815+
expectedChildren = new HashSet<string> () { sphereLOD0.name, capsuleLOD0.name, cubeLOD2.name };
816+
CompareGameObjectChildren (convertedHierarchy, expectedChildren);
817+
818+
fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject;
819+
Assert.IsTrue (fbxObj);
820+
821+
expectedChildren = new HashSet<string> () { sphereLOD0.name, capsuleLOD0.name, cubeLOD2.name };
822+
CompareGameObjectChildren (fbxObj, expectedChildren);
823+
}
824+
825+
826+
/// <summary>
827+
/// Compares obj's children to the expected children in the hashset.
828+
/// Doesn't recurse through the children.
829+
/// </summary>
830+
/// <param name="obj">Object.</param>
831+
/// <param name="expectedChildren">Expected children.</param>
832+
private void CompareGameObjectChildren(GameObject obj, HashSet<string> expectedChildren){
833+
Assert.That (obj.transform.childCount, Is.EqualTo (expectedChildren.Count));
834+
835+
foreach (Transform child in obj.transform) {
836+
Assert.That (expectedChildren.Contains (child.name));
837+
expectedChildren.Remove (child.name);
838+
}
839+
}
740840
}
741841
}

0 commit comments

Comments
 (0)