using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using AssetBank.Editor.ProjectExporter; using System; using UnityEditor.Animations; using UnityEngine.Audio; using UnityEngine.Video; namespace ProjectExporter { public class ProjectExporterController { private static readonly string ProjectRoot = Path.GetDirectoryName(Application.dataPath); private readonly ProjectExporterSettings _settings; public ProjectExporterController() { _settings = ProjectExporterSettings.GetOrCreateSettings(); } // Finds assets based on a search filter (e.g., "t:Scene") and builds a hierarchical model. public AssetModel GetAssets(string filter) { var root = new AssetModel("Assets", "Assets"); var assetGuids = AssetDatabase.FindAssets(filter, new[] { "Assets" }); foreach (var guid in assetGuids) { var path = AssetDatabase.GUIDToAssetPath(guid); if (filter.Contains("t:ScriptableObject") && !path.EndsWith(".asset")) { continue; } var parts = path.Split('/'); var currentNode = root; for (int i = 1; i < parts.Length; i++) // Start at 1 to skip "Assets" { var part = parts[i]; var child = currentNode.Children.FirstOrDefault(c => c.Name == part); if (child == null) { var currentPath = string.Join("/", parts.Take(i + 1)); child = new AssetModel(currentPath, part); currentNode.Children.Add(child); } currentNode = child; } } return root; } // Lists all files in the ProjectSettings directory. public AssetModel GetProjectSettingsFiles() { var settingsRootPath = Path.Combine(ProjectRoot, "ProjectSettings"); var root = new AssetModel(settingsRootPath, "ProjectSettings"); if (Directory.Exists(settingsRootPath)) { // Filter to only include *.asset files. var files = Directory.GetFiles(settingsRootPath, "*.asset", SearchOption.AllDirectories); foreach (var file in files) { var relativePath = Path.GetRelativePath(ProjectRoot, file); root.Children.Add(new AssetModel(relativePath, Path.GetFileName(file))); } } return root; } // Scans the entire Assets folder for .meta files and builds a hierarchical model. public AssetModel GetAllMetaFiles() { var assetsRootPath = Application.dataPath; // This is the "Assets" folder var root = new AssetModel("Assets", "Assets"); var files = Directory.GetFiles(assetsRootPath, "*.meta", SearchOption.AllDirectories); foreach (var file in files) { var relativePath = "Assets/" + Path.GetRelativePath(assetsRootPath, file).Replace('\\', '/'); var parts = relativePath.Split('/'); var currentNode = root; for (int i = 1; i < parts.Length; i++) // Start at 1 to skip "Assets" { var part = parts[i]; var child = currentNode.Children.FirstOrDefault(c => c.Name == part); if (child == null) { var currentPath = string.Join("/", parts.Take(i + 1)); child = new AssetModel(currentPath, part); currentNode.Children.Add(child); } currentNode = child; } } return root; } private string FindPythonExecutable() { var venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python3"); if (File.Exists(venvPath)) { return venvPath; } venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python"); if (File.Exists(venvPath)) { return venvPath; } return "python3"; } private string GetConversionScriptPath() { var guids = AssetDatabase.FindAssets("convert_scene"); if (guids.Length == 0) { UnityEngine.Debug.LogError("Conversion script 'convert_scene.py' not found."); return null; } var path = AssetDatabase.GUIDToAssetPath(guids[0]); return Path.Combine(ProjectRoot, path); } // Executes the Python conversion script for the selected assets. public void ExportAssets(AssetModel rootNode, string exportType) { var assetsToExport = new List(); CollectSelectedAssets(rootNode, assetsToExport); var filteredAssets = FilterAssets(assetsToExport); var pythonExecutable = FindPythonExecutable(); var conversionScript = GetConversionScriptPath(); if (string.IsNullOrEmpty(conversionScript)) return; var outputDirectory = Path.Combine(ProjectRoot, "Library", "ProjectDataExport"); Directory.CreateDirectory(outputDirectory); EditorUtility.DisplayProgressBar("Exporting...", "Starting export process...", 0f); try { for (int i = 0; i < filteredAssets.Count; i++) { var relativeAssetPath = filteredAssets[i]; var progress = (float)i / filteredAssets.Count; EditorUtility.DisplayProgressBar("Exporting...", $"Processing {Path.GetFileName(relativeAssetPath)}", progress); var absoluteAssetPath = Path.Combine(ProjectRoot, relativeAssetPath); var outputJsonPath = Path.Combine(outputDirectory, relativeAssetPath.Replace('/', Path.DirectorySeparatorChar) + ".json"); var fileOutputDir = Path.GetDirectoryName(outputJsonPath); if (!Directory.Exists(fileOutputDir)) { Directory.CreateDirectory(fileOutputDir); } var process = new Process { StartInfo = new ProcessStartInfo { FileName = pythonExecutable, Arguments = $"\"{conversionScript}\" \"{absoluteAssetPath}\" \"{outputJsonPath}\"", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true } }; process.Start(); string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); if (process.ExitCode != 0) { UnityEngine.Debug.LogError($"Error exporting {relativeAssetPath}: {error}"); } else { UnityEngine.Debug.Log($"Successfully exported {relativeAssetPath}: {output}"); } } } finally { EditorUtility.ClearProgressBar(); } } private List FilterAssets(List assetPaths) { if (_settings == null) { UnityEngine.Debug.LogWarning("ProjectExporterSettings not found. Skipping filtering."); return assetPaths; } var ignoredFolders = _settings.FoldersToIgnore; var ignoredExtensions = _settings.FileExtensionsToIgnore.Select(ext => ext.StartsWith(".") ? ext : "." + ext).ToList(); var ignoredUnityTypes = _settings.UnityTypesToIgnore; var filteredList = new List(); foreach (var path in assetPaths) { if (ignoredFolders.Any(folder => path.StartsWith(folder, StringComparison.OrdinalIgnoreCase))) { continue; } var extension = Path.GetExtension(path); if (ignoredExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)) { continue; } var assetPathForTypeCheck = path.EndsWith(".meta") ? path[..^5] : path; var assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPathForTypeCheck); if (assetType != null && IsTypeIgnored(assetType, ignoredUnityTypes)) { continue; } filteredList.Add(path); } return filteredList; } private bool IsTypeIgnored(Type assetType, List ignoredTypes) { foreach (var ignoredType in ignoredTypes) { if (ignoredType.assetType == UnityAssetType.Custom) { if (!string.IsNullOrEmpty(ignoredType.customType) && assetType.Name.Equals(ignoredType.customType, StringComparison.OrdinalIgnoreCase)) { return true; } } else if (GetSystemTypeForEnum(ignoredType.assetType) == assetType) { return true; } } return false; } private Type GetSystemTypeForEnum(UnityAssetType unityAssetType) { switch (unityAssetType) { case UnityAssetType.Animation: return typeof(AnimationClip); case UnityAssetType.AnimatorController: return typeof(AnimatorController); case UnityAssetType.AnimatorOverrideController: return typeof(AnimatorOverrideController); case UnityAssetType.AudioClip: return typeof(AudioClip); case UnityAssetType.AudioMixer: return typeof(AudioMixer); case UnityAssetType.ComputeShader: return typeof(ComputeShader); case UnityAssetType.Font: return typeof(Font); case UnityAssetType.GUISkin: return typeof(GUISkin); case UnityAssetType.Material: return typeof(Material); case UnityAssetType.Mesh: return typeof(Mesh); case UnityAssetType.Model: return typeof(GameObject); case UnityAssetType.PhysicMaterial: return typeof(PhysicMaterial); case UnityAssetType.Prefab: return typeof(GameObject); case UnityAssetType.Scene: return typeof(SceneAsset); case UnityAssetType.Script: return typeof(MonoScript); case UnityAssetType.Shader: return typeof(Shader); case UnityAssetType.Sprite: return typeof(Sprite); case UnityAssetType.Texture: return typeof(Texture2D); case UnityAssetType.VideoClip: return typeof(VideoClip); case UnityAssetType.RenderTexture: return typeof(RenderTexture); case UnityAssetType.LightmapParameters: return typeof(LightmapParameters); default: return null; } } // Recursively collects the paths of all selected assets. private void CollectSelectedAssets(AssetModel node, List selectedPaths) { // If the node itself is a file and is selected, add it. if (node.Children.Count == 0 && node.IsSelected) { selectedPaths.Add(node.Path); } // Recurse into children foreach (var child in node.Children) { CollectSelectedAssets(child, selectedPaths); } } } }