private HashSet <string> GetAnimatedGameObjects(AnimationClip[] animClips, GameObject animatorObject) { var animatedObjects = new HashSet <string>(); foreach (var clip in animClips) { foreach (EditorCurveBinding uniCurveBinding in AnimationUtility.GetCurveBindings(clip)) { Object uniObj = AnimationUtility.GetAnimatedObject(animatorObject, uniCurveBinding); if (!uniObj) { continue; } GameObject unityGo = ModelExporter.GetGameObject(uniObj); if (!unityGo) { continue; } // also it's parents up until but excluding the root (the root will have a different name) var parent = unityGo.transform; while (parent != null && parent.parent != null) { animatedObjects.Add(parent.name); parent = parent.parent; } } } return(animatedObjects); }
protected bool SelectionContainsPrefabInstanceWithAddedObjects() { var exportSet = GetToExport(); Stack <Object> stack = new Stack <Object>(exportSet); while (stack.Count > 0) { var go = ModelExporter.GetGameObject(stack.Pop()); if (!go) { continue; } if (PrefabUtility.IsAnyPrefabInstanceRoot(go) && PrefabUtility.GetAddedGameObjects(go).Count > 0) { return(true); } foreach (Transform child in go.transform) { stack.Push(child.gameObject); } } return(false); }
protected void SetGameObjectsToConvert(IEnumerable <GameObject> toConvert) { ToExport = toConvert.OrderBy(go => go.name).ToArray(); TransferAnimationSource = null; TransferAnimationDest = null; var toExport = ToExport; string fbxFileName = null; if (toExport.Length == 1) { var go = ModelExporter.GetGameObject(toExport[0]); // check if the GameObject is a model instance, use as default filename and path if it is GameObject mainAsset = ConvertToNestedPrefab.GetFbxAssetOrNull(go); if (!mainAsset) { // Use the game object's name m_prefabFileName = go.name; } else { // Use the asset's name var mainAssetRelPath = AssetDatabase.GetAssetPath(mainAsset); // remove Assets/ from beginning of path mainAssetRelPath = mainAssetRelPath.Substring("Assets".Length); m_prefabFileName = System.IO.Path.GetFileNameWithoutExtension(mainAssetRelPath); ExportSettings.AddFbxSavePath(System.IO.Path.GetDirectoryName(mainAssetRelPath)); fbxFileName = m_prefabFileName; } var fullPrefabPath = System.IO.Path.Combine(ExportSettings.PrefabAbsoluteSavePath, m_prefabFileName + ".prefab"); if (System.IO.File.Exists(fullPrefabPath)) { m_prefabFileName = System.IO.Path.GetFileNameWithoutExtension(ConvertToNestedPrefab.IncrementFileName(ExportSettings.PrefabAbsoluteSavePath, m_prefabFileName + ".prefab")); } // if only one object selected, set transfer source/dest to this object if (go) { TransferAnimationSource = go.transform; TransferAnimationDest = go.transform; } } else if (toExport.Length > 1) { m_prefabFileName = "(automatic)"; } // if there is an existing fbx file then use its name, otherwise use the same name as for the prefab this.SetFilename(fbxFileName != null? fbxFileName : m_prefabFileName); }
protected bool ExportSetContainsAnimation() { foreach (var obj in GetToExport()) { var go = ModelExporter.GetGameObject(obj); if (go.GetComponentInChildren <Animation>() || go.GetComponentInChildren <Animator>()) { return(true); } } return(false); }
/// <summary> /// Return the number of objects in the selection that contain RectTransforms. /// </summary> protected int GetUIElementsInExportSetCount() { int count = 0; foreach (var obj in GetToExport()) { var go = ModelExporter.GetGameObject(obj); var rectTransforms = go.GetComponentsInChildren <RectTransform>(); count += rectTransforms.Length; } return(count); }
/// <summary> /// collect all object dependencies for given animation clip /// </summary> public void CollectDependencies( AnimationClip animClip, GameObject rootObject, IExportOptions exportOptions ) { Debug.Assert(rootObject != null); Debug.Assert(exportOptions != null); if (this.animationClips.ContainsKey(animClip)) { // we have already exported gameobjects for this clip return; } // NOTE: the object (animationRootObject) containing the animation is not necessarily animated // when driven by an animator or animation component. this.animationClips.Add(animClip, rootObject); foreach (EditorCurveBinding uniCurveBinding in AnimationUtility.GetCurveBindings(animClip)) { Object uniObj = AnimationUtility.GetAnimatedObject(rootObject, uniCurveBinding); if (!uniObj) { continue; } GameObject unityGo = ModelExporter.GetGameObject(uniObj); if (!unityGo) { continue; } if (!exportOptions.AnimateSkinnedMesh && unityGo.GetComponent <SkinnedMeshRenderer>()) { continue; } // If we have a clip driving a camera or light then force the export of FbxNodeAttribute // so that they point the right way when imported into Maya. if (unityGo.GetComponent <Light>()) { this.exportComponent[unityGo] = typeof(Light); } else if (unityGo.GetComponent <Camera>()) { this.exportComponent[unityGo] = typeof(Camera); } this.goExportSet.Add(unityGo); } }
/// <summary> /// Return true if the given set contains only prefab assets on disk, /// and nothing from the scene. /// </summary> /// <returns></returns> internal static bool SetContainsOnlyPrefabAssets(Object[] toConvert) { foreach (var obj in toConvert) { var go = ModelExporter.GetGameObject(obj); if (go != null && !PrefabUtility.IsPartOfPrefabAsset(go)) { // return as soon as we find something that is not part of a prefab asset // on disk return(false); } } return(true); }
/// <summary> /// For all scene objects holding a reference to origObj, replaces the references to newObj. /// /// If one of the scene objects is toConvertRoot or a child of it, then do not fix its references as it /// will be deleted after conversion. /// </summary> /// <param name="origObj"></param> /// <param name="newObj"></param> /// <param name="toConvertRoot"></param> internal static void FixSceneReferences(Object origObj, Object newObj, GameObject toConvertRoot) { var sceneObjs = GetSceneReferencesToObject(origObj); // try to fix references on each component of each scene object, if applicable foreach (var sceneObj in sceneObjs) { var go = ModelExporter.GetGameObject(sceneObj); if (go && go.transform.IsChildOf(toConvertRoot.transform)) { // if this is a child of what we are converting, don't update its references. continue; } var components = sceneObj.GetComponents <Component>(); foreach (var component in components) { var serializedComponent = new SerializedObject(component); var property = serializedComponent.GetIterator(); property.Next(true); // skip generic field // For SkinnedMeshRenderer, the bones array doesn't have visible children, but may have references that need to be fixed. // For everything else, filtering by visible children in the while loop and then copying properties that don't have visible children, // ensures that only the leaf properties are copied over. Copying other properties is not usually necessary and may break references that // were not meant to be copied. while (property.Next((component is SkinnedMeshRenderer) ? property.hasChildren : property.hasVisibleChildren)) { if (!property.hasVisibleChildren) { // with Undo operations, copying m_Father reference causes issues. Also, it is not required as the reference is fixed when // the transform is parented under the correct hierarchy (which happens before this). if (property.propertyType == SerializedPropertyType.ObjectReference && property.propertyPath != "m_GameObject" && property.propertyPath != "m_Father" && property.objectReferenceValue && (property.objectReferenceValue == origObj)) { property.objectReferenceValue = newObj; serializedComponent.ApplyModifiedProperties(); } } } } } }
protected void SetGameObjectsToConvert(IEnumerable <GameObject> toConvert) { SetToExport(toConvert.OrderBy(go => go.name).ToArray()); TransferAnimationSource = null; TransferAnimationDest = null; if (GetToExport().Length == 1) { var go = ModelExporter.GetGameObject(GetToExport()[0]); // check if the GameObject is a model instance, use as default filename and path if it is var mainAsset = ConvertToModel.GetFbxAssetOrNull(go); if (!mainAsset) { // Use the game object's name m_prefabFileName = go.name; } else { // Use the asset's name var mainAssetRelPath = AssetDatabase.GetAssetPath(mainAsset); // remove Assets/ from beginning of path mainAssetRelPath = mainAssetRelPath.Substring("Assets".Length); m_prefabFileName = System.IO.Path.GetFileNameWithoutExtension(mainAssetRelPath); ExportSettings.AddFbxSavePath(System.IO.Path.GetDirectoryName(mainAssetRelPath)); } // if only one object selected, set transfer source/dest to this object if (go) { TransferAnimationSource = go.transform; TransferAnimationDest = go.transform; } } else if (GetToExport().Length > 1) { m_prefabFileName = "(automatic)"; } this.SetFilename(m_prefabFileName); }
protected bool SelectionContainsPrefabInstanceWithAddedObjects() { var exportSet = GetToExport(); // FBX-60 (fogbug 1307749): // On Linux OnGUI() sometimes gets called a few times before // the export set is set and window.show() is called. // This leads to this function being called from OnGUI() with a // null or empty export set, and an ArgumentNullException when // creating the stack. // Check that the set exists and has values before creating the stack. if (exportSet == null || exportSet.Length <= 0) { return(false); } Stack <Object> stack = new Stack <Object>(exportSet); while (stack.Count > 0) { var go = ModelExporter.GetGameObject(stack.Pop()); if (!go) { continue; } if (PrefabUtility.IsAnyPrefabInstanceRoot(go) && PrefabUtility.GetAddedGameObjects(go).Count > 0) { return(true); } foreach (Transform child in go.transform) { stack.Push(child.gameObject); } } return(false); }
protected override bool Export() { if (string.IsNullOrEmpty(ExportFileName)) { Debug.LogError("FbxExporter: Please specify an fbx filename"); return(false); } if (string.IsNullOrEmpty(m_prefabFileName)) { Debug.LogError("FbxExporter: Please specify a prefab filename"); return(false); } var fbxDirPath = ExportSettings.FbxAbsoluteSavePath; var fbxPath = System.IO.Path.Combine(fbxDirPath, ExportFileName + ".fbx"); var prefabDirPath = ExportSettings.PrefabAbsoluteSavePath; var prefabPath = System.IO.Path.Combine(prefabDirPath, m_prefabFileName + ".prefab"); if (GetToExport() == null) { Debug.LogError("FbxExporter: missing object for conversion"); return(false); } int rectTransformCount = GetUIElementsInExportSetCount(); if (rectTransformCount > 0) { // Warn that UI elements will break if converted string warning = string.Format("Warning: UI Components (ie, RectTransform) are not saved when converting to FBX.\n{0} item(s) in the selection will lose their UI components.", rectTransformCount); bool result = UnityEditor.EditorUtility.DisplayDialog( string.Format("{0} Warning", ModelExporter.PACKAGE_UI_NAME), warning, "Convert and Lose UI", "Cancel"); if (!result) { return(false); } } if (SettingsObject.UseMayaCompatibleNames && SettingsObject.AllowSceneModification) { string warning = "Names of objects in the hierarchy may change with the Compatible Naming option turned on"; if (ExportSetContainsAnimation()) { warning = "Compatible Naming option turned on. Names of objects in hierarchy may change and break animations."; } // give a warning dialog that indicates that names in the scene may change int result = UnityEditor.EditorUtility.DisplayDialogComplex( string.Format("{0} Warning", ModelExporter.PACKAGE_UI_NAME), warning, "OK", "Turn off and continue", "Cancel" ); if (result == 1) { // turn compatible naming off SettingsObject.SetUseMayaCompatibleNames(false); } else if (result == 2) { return(false); } } if (GetToExport().Length == 1) { var go = ModelExporter.GetGameObject(GetToExport()[0]); // Check if we'll be clobbering files. If so, warn the user // first and let them cancel out. if (!OverwriteExistingFile(prefabPath)) { return(false); } if (ConvertToNestedPrefab.WillExportFbx(go)) { if (!OverwriteExistingFile(fbxPath)) { return(false); } } ConvertToNestedPrefab.Convert( go, fbxFullPath: fbxPath, prefabFullPath: prefabPath, exportOptions: ExportSettings.instance.ConvertToPrefabSettings.info ); return(true); } bool onlyPrefabAssets = ConvertToNestedPrefab.SetContainsOnlyPrefabAssets(GetToExport()); int groupIndex = -1; // no need to undo if we aren't converting anything that's in the scene if (!onlyPrefabAssets) { Undo.IncrementCurrentGroup(); groupIndex = Undo.GetCurrentGroup(); Undo.SetCurrentGroupName(ConvertToNestedPrefab.UndoConversionCreateObject); } foreach (var obj in GetToExport()) { // Convert, automatically choosing a file path that won't clobber any existing files. var go = ModelExporter.GetGameObject(obj); ConvertToNestedPrefab.Convert( go, fbxDirectoryFullPath: fbxDirPath, prefabDirectoryFullPath: prefabDirPath, exportOptions: ExportSettings.instance.ConvertToPrefabSettings.info ); } if (!onlyPrefabAssets && groupIndex >= 0) { Undo.CollapseUndoOperations(groupIndex); Undo.IncrementCurrentGroup(); } return(true); }
protected override bool Export() { if (string.IsNullOrEmpty(ExportFileName)) { Debug.LogError("FbxExporter: Please specify an fbx filename"); return(false); } if (string.IsNullOrEmpty(m_prefabFileName)) { Debug.LogError("FbxExporter: Please specify a prefab filename"); return(false); } var fbxDirPath = ExportSettings.FbxAbsoluteSavePath; var fbxPath = System.IO.Path.Combine(fbxDirPath, ExportFileName + ".fbx"); var prefabDirPath = ExportSettings.PrefabAbsoluteSavePath; var prefabPath = System.IO.Path.Combine(prefabDirPath, m_prefabFileName + ".prefab"); if (GetToExport() == null) { Debug.LogError("FbxExporter: missing object for conversion"); return(false); } if (SettingsObject.UseMayaCompatibleNames && SettingsObject.AllowSceneModification) { string warning = "Names of objects in the hierarchy may change with the Compatible Naming option turned on"; if (ExportSetContainsAnimation()) { warning = "Compatible Naming option turned on. Names of objects in hierarchy may change and break animations."; } // give a warning dialog that indicates that names in the scene may change int result = UnityEditor.EditorUtility.DisplayDialogComplex( string.Format("{0} Warning", ModelExporter.PACKAGE_UI_NAME), warning, "OK", "Turn off and continue", "Cancel" ); if (result == 1) { // turn compatible naming off SettingsObject.SetUseMayaCompatibleNames(false); } else if (result == 2) { return(false); } } if (GetToExport().Length == 1) { var go = ModelExporter.GetGameObject(GetToExport()[0]); // Check if we'll be clobbering files. If so, warn the user // first and let them cancel out. if (ConvertToModel.WillCreatePrefab(go)) { if (!OverwriteExistingFile(prefabPath)) { return(false); } } if (ConvertToModel.WillExportFbx(go)) { if (!OverwriteExistingFile(fbxPath)) { return(false); } } ConvertToModel.Convert( go, fbxFullPath: fbxPath, prefabFullPath: prefabPath, exportOptions: ExportSettings.instance.ConvertToPrefabSettings.info ); return(true); } foreach (var obj in GetToExport()) { // Convert, automatically choosing a file path that won't clobber any existing files. var go = ModelExporter.GetGameObject(obj); ConvertToModel.Convert( go, fbxDirectoryFullPath: fbxDirPath, prefabDirectoryFullPath: prefabDirPath, exportOptions: ExportSettings.instance.ConvertToPrefabSettings.info ); } return(true); }