From c752d07d66a94473e859643530c51a4e20d4d5e0 Mon Sep 17 00:00:00 2001 From: vkovec Date: Tue, 20 Feb 2018 15:35:49 -0500 Subject: [PATCH 1/6] add export setting option to select LOD to export --- Assets/FbxExporters/Editor/FbxExportSettings.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Assets/FbxExporters/Editor/FbxExportSettings.cs b/Assets/FbxExporters/Editor/FbxExportSettings.cs index 05983bec8..d2afcba30 100644 --- a/Assets/FbxExporters/Editor/FbxExportSettings.cs +++ b/Assets/FbxExporters/Editor/FbxExportSettings.cs @@ -62,6 +62,11 @@ public override void OnInspectorGUI() { exportSettings.ExportFormatSelection = EditorGUILayout.Popup(exportSettings.ExportFormatSelection, new string[]{"Binary", "ASCII"}); GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(new GUIContent("LOD Export:", "Select which LOD to export."), GUILayout.Width(LabelWidth - FieldOffset)); + exportSettings.lodExportType = (ExportSettings.LODExportType)EditorGUILayout.Popup((int)exportSettings.lodExportType, new string[]{"All", "Highest", "Lowest"}); + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(); EditorGUILayout.LabelField(new GUIContent( "Export Path:", @@ -442,6 +447,15 @@ public static string[] DCCVendorLocations public int selectedDCCApp = 0; + [SerializeField] + public LODExportType lodExportType = LODExportType.All; + + public enum LODExportType { + All = 0, + Highest = 1, + Lowest = 2 + } + /// /// The path where Convert To Model will save the new fbx and prefab. /// From 0d2fc9923c005cac1ebff9e714f1f2b8cdfcc331 Mon Sep 17 00:00:00 2001 From: vkovec Date: Wed, 21 Feb 2018 12:34:44 -0500 Subject: [PATCH 2/6] export LOD according to selected LOD type - if using Convert to Model, always export all LODs --- Assets/FbxExporters/Editor/ConvertToModel.cs | 2 +- Assets/FbxExporters/Editor/FbxExporter.cs | 65 +++++++++++++++++--- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/Assets/FbxExporters/Editor/ConvertToModel.cs b/Assets/FbxExporters/Editor/ConvertToModel.cs index 68f28cb0a..90d1b3945 100644 --- a/Assets/FbxExporters/Editor/ConvertToModel.cs +++ b/Assets/FbxExporters/Editor/ConvertToModel.cs @@ -145,7 +145,7 @@ public static GameObject Convert ( // Export to FBX. It refreshes the database. { - var fbxActualPath = ModelExporter.ExportObject (fbxFullPath, toConvert); + var fbxActualPath = ModelExporter.ExportObject (fbxFullPath, toConvert, lodExportType: EditorTools.ExportSettings.LODExportType.All); if (fbxActualPath != fbxFullPath) { throw new System.Exception ("Failed to convert " + toConvert.name); } diff --git a/Assets/FbxExporters/Editor/FbxExporter.cs b/Assets/FbxExporters/Editor/FbxExporter.cs index 69b721c8c..d7c14c72a 100644 --- a/Assets/FbxExporters/Editor/FbxExporter.cs +++ b/Assets/FbxExporters/Editor/FbxExporter.cs @@ -7,6 +7,7 @@ using System.Linq; using UnityEngine.Playables; using UnityEngine.Timeline; +using FbxExporters.EditorTools; namespace FbxExporters { @@ -1922,7 +1923,9 @@ private string GetUniqueName(string name) protected int ExportTransformHierarchy( GameObject unityGo, FbxScene fbxScene, FbxNode fbxNodeParent, int exportProgress, int objectCount, Vector3 newCenter, - TransformExportType exportType = TransformExportType.Local) + TransformExportType exportType = TransformExportType.Local, + ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All + ) { int numObjectsExported = exportProgress; @@ -1957,9 +1960,43 @@ protected int ExportTransformHierarchy( fbxNodeParent.AddChild (fbxNode); + // if this object has an LOD group, then export according to the LOD preference setting + var lodGroup = unityGo.GetComponent(); + if (lodGroup && lodExportType != ExportSettings.LODExportType.All) { + LOD[] lods = lodGroup.GetLODs (); + + // LODs are ordered from highest to lowest. + // If exporting lowest LOD, reverse the array + if (lodExportType == ExportSettings.LODExportType.Lowest) { + // reverse the array + LOD[] tempLods = new LOD[lods.Length]; + System.Array.Copy (lods, tempLods, lods.Length); + System.Array.Reverse (tempLods); + lods = tempLods; + } + + for(int i = 0; i < lods.Length; i++){ + var lod = lods [i]; + bool exportedRenderer = false; + foreach (var renderer in lod.renderers) { + // only export if parented under LOD group + if (renderer.transform.parent == unityGo.transform) { + numObjectsExported = ExportTransformHierarchy (renderer.gameObject, fbxScene, fbxNode, numObjectsExported, objectCount, newCenter, lodExportType: lodExportType); + exportedRenderer = true; + } + } + + // if at least one renderer for this LOD was exported, then we succeeded + // so stop exporting. + if (exportedRenderer) { + return numObjectsExported; + } + } + } + // now unityGo through our children and recurse foreach (Transform childT in unityGo.transform) { - numObjectsExported = ExportTransformHierarchy (childT.gameObject, fbxScene, fbxNode, numObjectsExported, objectCount, newCenter); + numObjectsExported = ExportTransformHierarchy (childT.gameObject, fbxScene, fbxNode, numObjectsExported, objectCount, newCenter, lodExportType: lodExportType); } return numObjectsExported; @@ -2665,7 +2702,10 @@ public enum TransformExportType { Local, Global, Reset }; /// /// This refreshes the asset database. /// - public int ExportAll (IEnumerable unityExportSet, Dictionary animationExportData) + public int ExportAll ( + IEnumerable unityExportSet, + Dictionary animationExportData, + ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All) { exportCancelled = false; @@ -2782,7 +2822,7 @@ public int ExportAll (IEnumerable unityExportSet, Dictionary } else { exportProgress = this.ExportTransformHierarchy (unityGo, fbxScene, fbxRootNode, - exportProgress, count, center, exportType); + exportProgress, count, center, exportType, lodExportType); } if (exportCancelled || exportProgress < 0) { Debug.LogWarning ("Export Cancelled"); @@ -3547,7 +3587,7 @@ private static void OnExport (AnimationExportType exportType = AnimationExportTy return; } - if (ExportObjects (filePath, exportType: exportType) != null) { + if (ExportObjects (filePath, exportType: exportType, lodExportType: ExportSettings.instance.lodExportType) != null) { // refresh the asset database so that the file appears in the // asset folder view. AssetDatabase.Refresh (); @@ -3558,7 +3598,11 @@ private static void OnExport (AnimationExportType exportType = AnimationExportTy /// Export a list of (Game) objects to FBX file. /// Use the SaveFile panel to allow user to enter a file name. /// - public static string ExportObjects (string filePath, UnityEngine.Object[] objects = null, AnimationExportType exportType = AnimationExportType.all) + public static string ExportObjects ( + string filePath, + UnityEngine.Object[] objects = null, + AnimationExportType exportType = AnimationExportType.all, + ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All) { LastFilePath = filePath; @@ -3599,7 +3643,7 @@ public static string ExportObjects (string filePath, UnityEngine.Object[] object break; } - if (fbxExporter.ExportAll (objects, animationExportData) > 0) { + if (fbxExporter.ExportAll (objects, animationExportData, lodExportType) > 0) { string message = string.Format ("Successfully exported: {0}", filePath); UnityEngine.Debug.Log (message); @@ -3609,9 +3653,12 @@ public static string ExportObjects (string filePath, UnityEngine.Object[] object return null; } - public static string ExportObject (string filePath, UnityEngine.Object root, AnimationExportType exportType = AnimationExportType.all) + public static string ExportObject ( + string filePath, UnityEngine.Object root, + AnimationExportType exportType = AnimationExportType.all, + ExportSettings.LODExportType lodExportType = ExportSettings.LODExportType.All) { - return ExportObjects(filePath, new Object[] { root }, exportType); + return ExportObjects(filePath, new Object[] { root }, exportType, lodExportType); } private static void EnsureDirectory (string path) From 40befdae7930b63c682459324b27620f0bdfb3e9 Mon Sep 17 00:00:00 2001 From: vkovec Date: Wed, 21 Feb 2018 13:51:18 -0500 Subject: [PATCH 3/6] add LOD export unit tests --- .../Editor/UnitTests/ExporterTestBase.cs | 6 +- .../Editor/UnitTests/ModelExporterTest.cs | 93 +++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs b/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs index a856c0fef..03121ba68 100644 --- a/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs +++ b/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs @@ -258,10 +258,12 @@ protected virtual string ExportSelectedObjects(string filename, params Object[] /// The exported fbx file path. /// Hierarchy. /// If set to true export animation only. - protected string ExportToFbx (GameObject hierarchy, bool animOnly = false){ + protected string ExportToFbx (GameObject hierarchy, bool animOnly = false, EditorTools.ExportSettings.LODExportType lodExportType = EditorTools.ExportSettings.LODExportType.All){ string filename = GetRandomFbxFilePath (); var exportedFilePath = FbxExporters.Editor.ModelExporter.ExportObject ( - filename, hierarchy, animOnly? FbxExporters.Editor.ModelExporter.AnimationExportType.componentAnimation : FbxExporters.Editor.ModelExporter.AnimationExportType.all + filename, hierarchy, + animOnly? FbxExporters.Editor.ModelExporter.AnimationExportType.componentAnimation : FbxExporters.Editor.ModelExporter.AnimationExportType.all, + lodExportType ); Assert.That (exportedFilePath, Is.EqualTo (filename)); return filename; diff --git a/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs b/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs index ea55813b0..717c737e3 100644 --- a/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs +++ b/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs @@ -737,5 +737,98 @@ public void TestBlendShapeExport(string fbxPath) } } } + + [Test] + public void LODExportTest(){ + // Create the following test hierarchy: + // LODGroup + // -- Sphere_LOD0 + // -- Capsule_LOD0 + // -- Cube_LOD2 + // Cylinder_LOD1 + // + // where sphere + capsule renderers are both in LOD0, and cylinder is in LOD1 + // but not parented under the LOD group + + var lodGroup = new GameObject ("LODGroup"); + var sphereLOD0 = GameObject.CreatePrimitive (PrimitiveType.Sphere); + sphereLOD0.name = "Sphere_LOD0"; + var capsuleLOD0 = GameObject.CreatePrimitive (PrimitiveType.Capsule); + capsuleLOD0.name = "Capsule_LOD0"; + var cubeLOD2 = GameObject.CreatePrimitive (PrimitiveType.Cube); + cubeLOD2.name = "Cube_LOD2"; + var cylinderLOD1 = GameObject.CreatePrimitive (PrimitiveType.Cylinder); + cylinderLOD1.name = "Cylinder_LOD1"; + + sphereLOD0.transform.SetParent (lodGroup.transform); + capsuleLOD0.transform.SetParent (lodGroup.transform); + cubeLOD2.transform.SetParent (lodGroup.transform); + cylinderLOD1.transform.SetParent (null); + + // add LOD group + var lodGroupComp = lodGroup.AddComponent(); + Assert.That (lodGroupComp, Is.Not.Null); + + LOD[] lods = new LOD[3]; + lods [0] = new LOD (1, new Renderer[]{ sphereLOD0.GetComponent(), capsuleLOD0.GetComponent() }); + lods [1] = new LOD (0.75f, new Renderer[] { cylinderLOD1.GetComponent() }); + lods [2] = new LOD (0.5f, new Renderer[] { cubeLOD2.GetComponent() }); + lodGroupComp.SetLODs (lods); + lodGroupComp.RecalculateBounds (); + + // test export all + // expected LODs exported: Sphere_LOD0, Capsule_LOD0, Cube_LOD2 + string filename = ExportToFbx(lodGroup, lodExportType:EditorTools.ExportSettings.LODExportType.All); + GameObject fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject; + Assert.IsTrue (fbxObj); + + HashSet expectedChildren = new HashSet () { sphereLOD0.name, capsuleLOD0.name, cubeLOD2.name }; + CompareGameObjectChildren (fbxObj, expectedChildren); + + // test export highest + // expected LODs exported: Sphere_LOD0, Capsule_LOD0 + filename = ExportToFbx(lodGroup, lodExportType:EditorTools.ExportSettings.LODExportType.Highest); + fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject; + Assert.IsTrue (fbxObj); + + expectedChildren = new HashSet () { sphereLOD0.name, capsuleLOD0.name }; + CompareGameObjectChildren (fbxObj, expectedChildren); + + // test export lowest + // expected LODs exported: Cube_LOD2 + filename = ExportToFbx(lodGroup, lodExportType:EditorTools.ExportSettings.LODExportType.Lowest); + fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject; + Assert.IsTrue (fbxObj); + + expectedChildren = new HashSet () { cubeLOD2.name }; + CompareGameObjectChildren (fbxObj, expectedChildren); + + // test convert to prefab + // this should have the same result as "export all" + // expected LODs exported: Sphere_LOD0, Capsule_LOD0, Cube_LOD2 + // NOTE: Cylinder_LOD1 is not exported as it is not under the LODGroup hierarchy being exported + filename = GetRandomFbxFilePath(); + var convertedHierarchy = ConvertToModel.Convert(lodGroup, fbxFullPath: filename); + Assert.That (convertedHierarchy, Is.Not.Null); + + // check both converted hierarchy and fbx + expectedChildren = new HashSet () { sphereLOD0.name, capsuleLOD0.name, cubeLOD2.name }; + CompareGameObjectChildren (convertedHierarchy, expectedChildren); + + fbxObj = AssetDatabase.LoadMainAssetAtPath (filename) as GameObject; + Assert.IsTrue (fbxObj); + + expectedChildren = new HashSet () { sphereLOD0.name, capsuleLOD0.name, cubeLOD2.name }; + CompareGameObjectChildren (fbxObj, expectedChildren); + } + + private void CompareGameObjectChildren(GameObject obj, HashSet expectedChildren){ + Assert.That (obj.transform.childCount, Is.EqualTo (expectedChildren.Count)); + + foreach (Transform child in obj.transform) { + Assert.That (expectedChildren.Contains (child.name)); + expectedChildren.Remove (child.name); + } + } } } From 9693cc249a8eb30a2107e06c93b5d291c6d2d077 Mon Sep 17 00:00:00 2001 From: vkovec Date: Wed, 21 Feb 2018 13:52:21 -0500 Subject: [PATCH 4/6] add summary to function --- Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs b/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs index 717c737e3..b4583df7b 100644 --- a/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs +++ b/Assets/FbxExporters/Editor/UnitTests/ModelExporterTest.cs @@ -822,6 +822,13 @@ public void LODExportTest(){ CompareGameObjectChildren (fbxObj, expectedChildren); } + + /// + /// Compares obj's children to the expected children in the hashset. + /// Doesn't recurse through the children. + /// + /// Object. + /// Expected children. private void CompareGameObjectChildren(GameObject obj, HashSet expectedChildren){ Assert.That (obj.transform.childCount, Is.EqualTo (expectedChildren.Count)); From 0bdb0f2b8cec1099b8c14fa642df5b6858cfcdcb Mon Sep 17 00:00:00 2001 From: vkovec Date: Fri, 23 Feb 2018 10:43:36 -0500 Subject: [PATCH 5/6] add verbose message for skipped LODs --- Assets/FbxExporters/Editor/FbxExporter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/FbxExporters/Editor/FbxExporter.cs b/Assets/FbxExporters/Editor/FbxExporter.cs index d7c14c72a..ffc0c1e1e 100644 --- a/Assets/FbxExporters/Editor/FbxExporter.cs +++ b/Assets/FbxExporters/Editor/FbxExporter.cs @@ -1983,6 +1983,8 @@ protected int ExportTransformHierarchy( if (renderer.transform.parent == unityGo.transform) { numObjectsExported = ExportTransformHierarchy (renderer.gameObject, fbxScene, fbxNode, numObjectsExported, objectCount, newCenter, lodExportType: lodExportType); exportedRenderer = true; + } else if(Verbose) { + Debug.LogFormat ("FbxExporter: Not exporting LOD {0}: {1}", i, renderer.name); } } From 6f717f4b54885b9e19cc2f0e2871e09b2fe26f31 Mon Sep 17 00:00:00 2001 From: vkovec Date: Fri, 23 Feb 2018 13:07:37 -0500 Subject: [PATCH 6/6] fix compile error --- Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs b/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs index b3612d184..40a6521fd 100644 --- a/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs +++ b/Assets/FbxExporters/Editor/UnitTests/ExporterTestBase.cs @@ -266,7 +266,7 @@ protected string ExportToFbx (GameObject hierarchy, bool animOnly = false, Edito var exportedFilePath = FbxExporters.Editor.ModelExporter.ExportObject ( filename, hierarchy, animOnly? FbxExporters.Editor.ModelExporter.AnimationExportType.componentAnimation : FbxExporters.Editor.ModelExporter.AnimationExportType.all, - lodExportType + lodExportType: lodExportType ); Assert.That (exportedFilePath, Is.EqualTo (filename)); return filename;