Rasagar/Library/PackageCache/com.unity.formats.fbx/Editor/ConvertToPrefabEditorWindow.cs
2024-08-30 17:33:54 +03:00

429 lines
18 KiB
C#

using System.Collections.Generic;
using UnityEngine;
#if UNITY_2018_1_OR_NEWER
using UnityEditor.Presets;
#endif
using System.Linq;
namespace UnityEditor.Formats.Fbx.Exporter
{
internal class ConvertToPrefabEditorWindow : ExportOptionsEditorWindow
{
protected override GUIContent WindowTitle { get { return new GUIContent("Convert Options"); } }
protected override float MinWindowHeight { get { return 350; } } // determined by trial and error
protected override string ExportButtonName { get { return "Convert"; } }
private string m_prefabFileName = "";
private float m_prefabExtLabelWidth;
protected override bool DisableNameSelection
{
get
{
var toExport = ToExport;
return (toExport != null && toExport.Length > 1);
}
}
protected override bool DisableTransferAnim
{
get
{
var toExport = ToExport;
return toExport == null || toExport.Length > 1;
}
}
public static ConvertToPrefabEditorWindow Init(IEnumerable<GameObject> toConvert)
{
ConvertToPrefabEditorWindow window = CreateWindow<ConvertToPrefabEditorWindow>();
window.InitializeWindow();
window.SetGameObjectsToConvert(toConvert);
window.Show();
return window;
}
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 override void OnEnable()
{
base.OnEnable();
if (!InnerEditor)
{
InnerEditor = UnityEditor.Editor.CreateEditor(ConvertToPrefabSettingsInstance);
}
m_prefabExtLabelWidth = FbxExtLabelStyle.CalcSize(new GUIContent(".prefab")).x;
}
/// <summary>
/// Return the number of objects in the selection that contain RectTransforms.
/// </summary>
protected int GetUIElementsInExportSetCount()
{
int count = 0;
foreach (var obj in ToExport)
{
var go = ModelExporter.GetGameObject(obj);
var rectTransforms = go.GetComponentsInChildren<RectTransform>();
count += rectTransforms.Length;
}
return count;
}
protected bool ExportSetContainsAnimation()
{
foreach (var obj in ToExport)
{
var go = ModelExporter.GetGameObject(obj);
if (go.GetComponentInChildren<Animation>() || go.GetComponentInChildren<Animator>())
{
return true;
}
}
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.GetAbsoluteSavePath(FbxSavePaths[SelectedFbxPath]);;
var fbxPath = System.IO.Path.Combine(fbxDirPath, ExportFileName + ".fbx");
var prefabDirPath = ExportSettings.GetAbsoluteSavePath(PrefabSavePaths[SelectedPrefabPath]);
var prefabPath = System.IO.Path.Combine(prefabDirPath, m_prefabFileName + ".prefab");
if (ToExport == 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 (ToExport.Length == 1)
{
var go = ModelExporter.GetGameObject(ToExport[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: ConvertToPrefabSettingsInstance.info
);
return true;
}
bool onlyPrefabAssets = ConvertToNestedPrefab.SetContainsOnlyPrefabAssets(ToExport);
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 ToExport)
{
// 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: ConvertToPrefabSettingsInstance.info
);
}
if (!onlyPrefabAssets && groupIndex >= 0)
{
Undo.CollapseUndoOperations(groupIndex);
Undo.IncrementCurrentGroup();
}
return true;
}
public const string k_SessionStoragePrefix = "FbxExporterConvertOptions_{0}";
protected override string SessionStoragePrefix { get { return k_SessionStoragePrefix; } }
public override void ResetSessionSettings(string defaultSettings = null)
{
base.ResetSessionSettings(defaultSettings);
// save the source and dest as these are not serialized
var source = m_convertToPrefabSettingsInstance.info.AnimationSource;
var dest = m_convertToPrefabSettingsInstance.info.AnimationDest;
m_convertToPrefabSettingsInstance = null;
ConvertToPrefabSettingsInstance.info.SetAnimationSource(source);
ConvertToPrefabSettingsInstance.info.SetAnimationDest(dest);
m_prefabSavePaths = null;
SelectedPrefabPath = 0;
InnerEditor = Editor.CreateEditor(ConvertToPrefabSettingsInstance);
}
protected override void StoreSettingsInSession()
{
base.StoreSettingsInSession();
// store Prefab Save Paths
StorePathsInSession(k_SessionPrefabPathsName, m_prefabSavePaths);
SessionState.SetInt(string.Format(SessionStoragePrefix, k_SessionSelectedPrefabPathName), SelectedPrefabPath);
}
private List<string> m_prefabSavePaths;
internal List<string> PrefabSavePaths
{
get
{
if (m_prefabSavePaths == null)
{
// Try to restore from session, fall back to Fbx Export Settings
RestorePathsFromSession(k_SessionPrefabPathsName, ExportSettings.instance.GetCopyOfPrefabSavePaths(), out m_prefabSavePaths);
SelectedPrefabPath = SessionState.GetInt(string.Format(SessionStoragePrefix, k_SessionSelectedPrefabPathName), ExportSettings.instance.SelectedPrefabPath);
}
return m_prefabSavePaths;
}
}
[SerializeField]
private int m_selectedPrefabPath = 0;
internal int SelectedPrefabPath
{
get { return m_selectedPrefabPath; }
set { m_selectedPrefabPath = value; }
}
private ConvertToPrefabSettings m_convertToPrefabSettingsInstance;
public ConvertToPrefabSettings ConvertToPrefabSettingsInstance
{
get
{
if (m_convertToPrefabSettingsInstance == null)
{
// make a copy of the settings
m_convertToPrefabSettingsInstance = ScriptableObject.CreateInstance(typeof(ConvertToPrefabSettings)) as ConvertToPrefabSettings;
// load settings stored in Unity session, default to DefaultPreset, if none then Export Settings
var defaultPresets = Preset.GetDefaultPresetsForObject(m_convertToPrefabSettingsInstance);
if (defaultPresets.Length <= 0)
{
RestoreSettingsFromSession(ExportSettings.instance.ConvertToPrefabSettings.info);
}
else
{
// apply the first default preset
// TODO: figure out what it means to have multiple default presets, when would they be applied?
defaultPresets[0].ApplyTo(m_convertToPrefabSettingsInstance);
RestoreSettingsFromSession(m_convertToPrefabSettingsInstance.info);
}
}
return m_convertToPrefabSettingsInstance;
}
}
public override void SaveExportSettings()
{
// check if the settings are different from what is in the Project Settings and only store
// if they are. Otherwise we want to keep them updated with changes to the Project Settings.
bool settingsChanged = !(ConvertToPrefabSettingsInstance.Equals(ExportSettings.instance.ConvertToPrefabSettings));
var projectSettingsFbxPaths = ExportSettings.instance.GetCopyOfFbxSavePaths();
settingsChanged |= !projectSettingsFbxPaths.SequenceEqual(FbxSavePaths);
var projectSettingsPrefabPaths = ExportSettings.instance.GetCopyOfPrefabSavePaths();
settingsChanged |= !projectSettingsPrefabPaths.SequenceEqual(PrefabSavePaths);
settingsChanged |= SelectedPrefabPath != ExportSettings.instance.SelectedPrefabPath;
settingsChanged |= SelectedFbxPath != ExportSettings.instance.SelectedFbxPath;
if (settingsChanged)
{
StoreSettingsInSession();
}
}
protected override ExportOptionsSettingsSerializeBase SettingsObject
{
get { return ConvertToPrefabSettingsInstance.info; }
}
#if UNITY_2018_1_OR_NEWER
protected override void ShowPresetReceiver()
{
ShowPresetReceiver(ConvertToPrefabSettingsInstance);
}
#endif
protected override void CreateCustomUI()
{
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(new GUIContent(
"Prefab Name",
"Filename to save prefab to."), GUILayout.Width(LabelWidth - TextFieldAlignOffset));
EditorGUI.BeginDisabledGroup(DisableNameSelection);
// Show the export name with an uneditable ".prefab" at the end
//-------------------------------------
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal(EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
EditorGUI.indentLevel--;
// continually resize to contents
var textFieldSize = NameTextFieldStyle.CalcSize(new GUIContent(m_prefabFileName));
m_prefabFileName = EditorGUILayout.TextField(m_prefabFileName, NameTextFieldStyle, GUILayout.Width(textFieldSize.x + 5), GUILayout.MinWidth(5));
m_prefabFileName = ModelExporter.ConvertToValidFilename(m_prefabFileName);
EditorGUILayout.LabelField("<color=#808080ff>.prefab</color>", FbxExtLabelStyle, GUILayout.Width(m_prefabExtLabelWidth));
EditorGUI.indentLevel++;
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
//-----------------------------------
EditorGUI.EndDisabledGroup();
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(new GUIContent(
"Prefab Path",
"Relative path for saving FBX Prefab Variants."), GUILayout.Width(LabelWidth - FieldOffset));
var pathLabels = ExportSettings.GetRelativeSavePaths(PrefabSavePaths);
SelectedPrefabPath = EditorGUILayout.Popup(SelectedPrefabPath, pathLabels, GUILayout.MinWidth(SelectableLabelMinWidth));
if (GUILayout.Button(new GUIContent("...", "Browse to a new location to save prefab to"), EditorStyles.miniButton, GUILayout.Width(BrowseButtonWidth)))
{
string initialPath = Application.dataPath;
string fullPath = EditorUtility.SaveFolderPanel(
"Select FBX Prefab Variant Save Path", initialPath, null
);
// Unless the user canceled, make sure they chose something in the Assets folder.
if (!string.IsNullOrEmpty(fullPath))
{
var relativePath = ExportSettings.ConvertToAssetRelativePath(fullPath);
if (string.IsNullOrEmpty(relativePath))
{
Debug.LogWarning("Please select a location in the Assets folder");
}
else
{
ExportSettings.AddSavePath(relativePath, PrefabSavePaths);
SelectedPrefabPath = 0;
// Make sure focus is removed from the selectable label
// otherwise it won't update
GUIUtility.hotControl = 0;
GUIUtility.keyboardControl = 0;
}
}
}
GUILayout.EndHorizontal();
}
}
}