SanAndreasUnity/Assets/Scripts/Editor/AssetExporter.cs

305 lines
10 KiB
C#
Raw Normal View History

2022-01-16 02:52:59 +00:00
using SanAndreasUnity.Behaviours.World;
2022-01-16 04:00:29 +00:00
using SanAndreasUnity.Utilities;
2022-01-16 02:52:59 +00:00
using System.Collections;
using System.Collections.Generic;
2022-01-16 18:26:25 +00:00
using System.Diagnostics;
2022-01-16 02:52:59 +00:00
using System.IO;
2022-01-16 04:00:29 +00:00
using System.Linq;
2022-01-16 02:52:59 +00:00
using UnityEditor;
using UnityEngine;
namespace SanAndreasUnity.Editor
{
public class AssetExporter : EditorWindowBase
{
private const string DefaultFolderName = "ExportedAssets";
private string m_selectedFolder = "Assets/" + DefaultFolderName;
string ModelsPath => m_selectedFolder + "/Models";
string CollisionModelsPath => m_selectedFolder + "/CollisionModels";
string MaterialsPath => m_selectedFolder + "/Materials";
string TexturesPath => m_selectedFolder + "/Textures";
string PrefabsPath => m_selectedFolder + "/Prefabs";
private CoroutineInfo m_coroutineInfo;
2022-01-16 18:26:25 +00:00
private int m_numNewlyExportedAssets = 0;
private int m_numAlreadyExportedAssets = 0;
2022-01-16 04:00:29 +00:00
private bool m_exportFromSelection = false;
private bool m_exportCollisionMeshes = true;
2022-01-16 02:52:59 +00:00
[MenuItem(EditorCore.MenuName + "/" + "Asset exporter")]
static void Init()
{
var window = GetWindow<AssetExporter>();
window.Show();
}
public AssetExporter()
{
this.titleContent = new GUIContent("Asset exporter");
}
void OnGUI()
{
EditorGUILayout.HelpBox(
"This tool can export all currenty loaded world objects as assets and prefabs.\n" +
"It will store them in a separate folder, and will only export those objects that were not already exported.",
MessageType.Info,
true);
2022-01-16 17:06:25 +00:00
GUILayout.Space(30);
2022-01-16 02:52:59 +00:00
2022-01-16 16:22:53 +00:00
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Folder: " + m_selectedFolder);
if (GUILayout.Button("Change"))
this.ChangeFolder();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
m_exportCollisionMeshes = EditorGUILayout.Toggle("Export collision meshes", m_exportCollisionMeshes);
2022-01-16 17:06:25 +00:00
GUILayout.Space(30);
2022-01-16 16:22:53 +00:00
2022-01-16 04:00:29 +00:00
if (GUILayout.Button("Export from world"))
this.Export(false);
if (GUILayout.Button("Export from selection"))
this.Export(true);
2022-01-16 02:52:59 +00:00
}
2022-01-16 16:22:53 +00:00
void ChangeFolder()
{
2022-01-16 17:06:42 +00:00
string newFolder = EditorUtility.SaveFolderPanel(
2022-01-16 16:22:53 +00:00
"Select folder where to export files",
m_selectedFolder,
"");
2022-01-16 17:06:42 +00:00
if (string.IsNullOrWhiteSpace(newFolder))
2022-01-16 16:22:53 +00:00
{
return;
}
2022-01-16 17:06:42 +00:00
newFolder = FileUtil.GetProjectRelativePath(newFolder);
if (string.IsNullOrWhiteSpace(newFolder))
2022-01-16 16:22:53 +00:00
{
EditorUtility.DisplayDialog("", "Folder must be inside project.", "Ok");
}
2022-01-16 17:06:42 +00:00
m_selectedFolder = newFolder;
2022-01-16 16:22:53 +00:00
}
2022-01-16 04:00:29 +00:00
void Export(bool fromSelection)
2022-01-16 02:52:59 +00:00
{
if (this.IsCoroutineRunning(m_coroutineInfo))
return;
2022-01-16 04:00:29 +00:00
m_exportFromSelection = fromSelection;
2022-01-16 02:52:59 +00:00
m_coroutineInfo = this.StartCoroutine(this.ExportCoroutine(), this.Cleanup, ex => this.Cleanup());
}
void Cleanup()
{
EditorUtility.ClearProgressBar();
}
IEnumerator ExportCoroutine()
{
yield return null;
2022-01-16 18:26:25 +00:00
m_numNewlyExportedAssets = 0;
m_numAlreadyExportedAssets = 0;
2022-01-16 16:22:53 +00:00
if (string.IsNullOrWhiteSpace(m_selectedFolder))
{
EditorUtility.DisplayDialog("", "Select a folder first.", "Ok");
yield break;
}
2022-01-16 04:00:29 +00:00
if (m_exportFromSelection)
{
if (Selection.transforms.Length == 0)
{
EditorUtility.DisplayDialog("", "No object selected.", "Ok");
yield break;
}
}
2022-01-16 02:52:59 +00:00
var cell = Cell.Instance;
2022-01-16 04:00:29 +00:00
if (null == cell && !m_exportFromSelection)
2022-01-16 02:52:59 +00:00
{
EditorUtility.DisplayDialog("", $"{nameof(Cell)} script not found in scene. Make sure that you started the game with the correct scene.", "Ok");
yield break;
}
EditorUtility.DisplayProgressBar("", "Gathering info...", 0f);
2022-01-16 04:00:29 +00:00
Transform[] objectsToExport = m_exportFromSelection
? Selection.transforms
: cell.transform.GetFirstLevelChildren().ToArray();
2022-01-16 02:52:59 +00:00
int numObjectsActive = 0;
2022-01-16 04:00:29 +00:00
for (int i = 0; i < objectsToExport.Length; i++)
2022-01-16 02:52:59 +00:00
{
2022-01-16 04:00:29 +00:00
var child = objectsToExport[i];
2022-01-16 02:52:59 +00:00
if (child.gameObject.activeInHierarchy)
numObjectsActive++;
}
EditorUtility.ClearProgressBar();
if (!EditorUtility.DisplayDialog(
"",
2022-01-16 04:00:29 +00:00
$"There are {objectsToExport.Length} objects, with {numObjectsActive} active ones.\nProceed ?",
2022-01-16 02:52:59 +00:00
"Ok",
"Cancel"))
{
yield break;
}
if (EditorApplication.isPlaying)
EditorApplication.isPaused = true;
2022-01-16 18:26:25 +00:00
var stopwatch = Stopwatch.StartNew();
2022-01-16 02:52:59 +00:00
EditorUtility.DisplayProgressBar("", "Creating folders...", 0f);
this.CreateFolders();
EditorUtility.DisplayProgressBar("", "Creating assets...", 0f);
int numExported = 0;
2022-01-16 04:00:29 +00:00
for (int i = 0; i < objectsToExport.Length; i++)
2022-01-16 02:52:59 +00:00
{
2022-01-16 04:00:29 +00:00
var child = objectsToExport[i];
2022-01-16 02:52:59 +00:00
if (!child.gameObject.activeInHierarchy)
continue;
2022-01-16 04:13:55 +00:00
if (EditorUtility.DisplayCancelableProgressBar("", $"Creating assets ({numExported}/{numObjectsActive})... {child.name}", numExported / (float)numObjectsActive))
2022-01-16 02:52:59 +00:00
yield break;
this.ExportAssets(child.gameObject);
numExported++;
2022-01-16 04:00:29 +00:00
yield return null;
2022-01-16 02:52:59 +00:00
}
EditorUtility.DisplayProgressBar("", "Creating prefab...", 1f);
2022-01-16 04:00:29 +00:00
if (!m_exportFromSelection)
PrefabUtility.SaveAsPrefabAsset(cell.gameObject, $"{PrefabsPath}/{cell.gameObject.name}.prefab");
else
{
foreach (var obj in objectsToExport)
{
PrefabUtility.SaveAsPrefabAsset(obj.gameObject, $"{PrefabsPath}/{obj.gameObject.name}.prefab");
}
}
2022-01-16 02:52:59 +00:00
EditorUtility.DisplayProgressBar("", "Refreshing asset database...", 1f);
AssetDatabase.Refresh();
EditorUtility.ClearProgressBar();
2022-01-16 18:26:25 +00:00
string displayText = $"number of newly exported asssets {m_numNewlyExportedAssets}, number of already exported assets {m_numAlreadyExportedAssets}, time elapsed {stopwatch.Elapsed}";
UnityEngine.Debug.Log($"Exporting of assets finished, {displayText}");
EditorUtility.DisplayDialog("", $"Finished ! \r\n{displayText}", "Ok");
2022-01-16 02:52:59 +00:00
}
void CreateFolders()
{
2022-01-16 17:07:06 +00:00
if (!Directory.Exists(m_selectedFolder))
Directory.CreateDirectory(m_selectedFolder);
2022-01-16 02:52:59 +00:00
string[] folders = new string[]
{
"Models",
"Materials",
"Textures",
"Prefabs",
"CollisionModels",
};
foreach (string folder in folders)
{
if (!AssetDatabase.IsValidFolder(Path.Combine(m_selectedFolder, folder)))
AssetDatabase.CreateFolder(m_selectedFolder, folder);
}
}
public void ExportAssets(GameObject go)
{
string assetName = go.name;
var meshFilters = go.GetComponentsInChildren<MeshFilter>();
for (int i = 0; i < meshFilters.Length; i++)
{
MeshFilter meshFilter = meshFilters[i];
string indexPath = meshFilters.Length == 1 ? "" : "-" + i;
2022-01-16 16:14:28 +00:00
meshFilter.sharedMesh = (Mesh)CreateAssetIfNotExists(meshFilter.sharedMesh, $"{ModelsPath}/{assetName}{indexPath}.asset");
2022-01-16 02:52:59 +00:00
}
var meshRenderers = go.GetComponentsInChildren<MeshRenderer>();
for (int i = 0; i < meshRenderers.Length; i++)
{
ExportMeshRenderer(go, meshRenderers[i], meshRenderers.Length == 1 ? (int?)null : i);
}
if (m_exportCollisionMeshes)
2022-01-16 02:52:59 +00:00
{
var meshColliders = go.GetComponentsInChildren<MeshCollider>();
for (int i = 0; i < meshColliders.Length; i++)
{
string indexPath = meshColliders.Length == 1 ? "" : "-" + i;
meshColliders[i].sharedMesh = (Mesh)CreateAssetIfNotExists(meshColliders[i].sharedMesh, $"{CollisionModelsPath}/{assetName}{indexPath}.asset");
}
2022-01-16 02:52:59 +00:00
}
//PrefabUtility.SaveAsPrefabAsset(go, $"{PrefabsPath}/{assetName}.prefab");
}
public void ExportMeshRenderer(GameObject rootGo, MeshRenderer meshRenderer, int? index)
{
string indexPath = index.HasValue ? "-" + index.Value : "";
string assetName = rootGo.name + indexPath;
2022-01-16 16:14:28 +00:00
var mats = meshRenderer.sharedMaterials.ToArray();
2022-01-16 02:52:59 +00:00
for (int i = 0; i < mats.Length; i++)
{
2022-01-16 04:01:46 +00:00
var tex = mats[i].mainTexture;
2022-01-16 17:51:20 +00:00
if (tex != null && tex != Texture2D.whiteTexture) // sometimes materials will have white texture assigned, and Unity will crash if we attempt to create asset from it
2022-01-16 16:14:28 +00:00
mats[i].mainTexture = (Texture)CreateAssetIfNotExists(tex, $"{TexturesPath}/{assetName}-{i}.asset");
mats[i] = (Material)CreateAssetIfNotExists(mats[i], $"{MaterialsPath}/{assetName}-{i}.mat");
2022-01-16 02:52:59 +00:00
}
2022-01-16 16:14:28 +00:00
meshRenderer.sharedMaterials = mats;
2022-01-16 02:52:59 +00:00
}
2022-01-16 18:26:25 +00:00
private Object CreateAssetIfNotExists(Object asset, string path)
2022-01-16 02:52:59 +00:00
{
if (AssetDatabase.Contains(asset))
2022-01-16 16:14:28 +00:00
return asset;
2022-01-16 02:52:59 +00:00
2022-01-16 03:26:03 +00:00
if (File.Exists(Path.Combine(Application.dataPath + "/../", path)))
2022-01-16 18:26:25 +00:00
{
m_numAlreadyExportedAssets++;
2022-01-16 16:14:28 +00:00
return AssetDatabase.LoadMainAssetAtPath(path);
2022-01-16 18:26:25 +00:00
}
2022-01-16 02:52:59 +00:00
2022-01-16 03:26:03 +00:00
AssetDatabase.CreateAsset(asset, path);
2022-01-16 16:14:28 +00:00
2022-01-16 18:26:25 +00:00
m_numNewlyExportedAssets++;
2022-01-16 16:14:28 +00:00
return AssetDatabase.LoadMainAssetAtPath(path);
2022-01-16 02:52:59 +00:00
}
}
}