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 ;
2022-01-16 16:31:57 +00:00
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 ( ) ;
2022-01-16 16:31:57 +00:00
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 ) ;
}
2022-01-16 16:31:57 +00:00
if ( m_exportCollisionMeshes )
2022-01-16 02:52:59 +00:00
{
2022-01-16 16:31:57 +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
}
}
}