ProjectExporterController.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. using System.Collections.Generic;
  2. using System.Diagnostics;
  3. using System.IO;
  4. using System.Linq;
  5. using UnityEditor;
  6. using UnityEngine;
  7. namespace ProjectExporter
  8. {
  9. public class ProjectExporterController
  10. {
  11. private static readonly string ProjectRoot = Path.GetDirectoryName(Application.dataPath);
  12. // Finds assets based on a search filter (e.g., "t:Scene") and builds a hierarchical model.
  13. public AssetModel GetAssets(string filter)
  14. {
  15. var root = new AssetModel("Assets", "Assets");
  16. var assetGuids = AssetDatabase.FindAssets(filter, new[] { "Assets" });
  17. foreach (var guid in assetGuids)
  18. {
  19. var path = AssetDatabase.GUIDToAssetPath(guid);
  20. if (filter.Contains("t:ScriptableObject") && !path.EndsWith(".asset"))
  21. {
  22. continue;
  23. }
  24. var parts = path.Split('/');
  25. var currentNode = root;
  26. for (int i = 1; i < parts.Length; i++) // Start at 1 to skip "Assets"
  27. {
  28. var part = parts[i];
  29. var child = currentNode.Children.FirstOrDefault(c => c.Name == part);
  30. if (child == null)
  31. {
  32. var currentPath = string.Join("/", parts.Take(i + 1));
  33. child = new AssetModel(currentPath, part);
  34. currentNode.Children.Add(child);
  35. }
  36. currentNode = child;
  37. }
  38. }
  39. return root;
  40. }
  41. // Lists all files in the ProjectSettings directory.
  42. public AssetModel GetProjectSettingsFiles()
  43. {
  44. var settingsRootPath = Path.Combine(ProjectRoot, "ProjectSettings");
  45. var root = new AssetModel(settingsRootPath, "ProjectSettings");
  46. if (Directory.Exists(settingsRootPath))
  47. {
  48. // Filter to only include *.asset files.
  49. var files = Directory.GetFiles(settingsRootPath, "*.asset", SearchOption.AllDirectories);
  50. foreach (var file in files)
  51. {
  52. var relativePath = Path.GetRelativePath(ProjectRoot, file);
  53. root.Children.Add(new AssetModel(relativePath, Path.GetFileName(file)));
  54. }
  55. }
  56. return root;
  57. }
  58. // Scans the entire Assets folder for .meta files and builds a hierarchical model.
  59. public AssetModel GetAllMetaFiles()
  60. {
  61. var assetsRootPath = Application.dataPath; // This is the "Assets" folder
  62. var root = new AssetModel("Assets", "Assets");
  63. var files = Directory.GetFiles(assetsRootPath, "*.meta", SearchOption.AllDirectories);
  64. foreach (var file in files)
  65. {
  66. var relativePath = "Assets/" + Path.GetRelativePath(assetsRootPath, file).Replace('\\', '/');
  67. var parts = relativePath.Split('/');
  68. var currentNode = root;
  69. for (int i = 1; i < parts.Length; i++) // Start at 1 to skip "Assets"
  70. {
  71. var part = parts[i];
  72. var child = currentNode.Children.FirstOrDefault(c => c.Name == part);
  73. if (child == null)
  74. {
  75. var currentPath = string.Join("/", parts.Take(i + 1));
  76. child = new AssetModel(currentPath, part);
  77. currentNode.Children.Add(child);
  78. }
  79. currentNode = child;
  80. }
  81. }
  82. return root;
  83. }
  84. private string FindPythonExecutable()
  85. {
  86. var venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python3");
  87. if (File.Exists(venvPath))
  88. {
  89. return venvPath;
  90. }
  91. venvPath = Path.Combine(ProjectRoot, "venv", "bin", "python");
  92. if (File.Exists(venvPath))
  93. {
  94. return venvPath;
  95. }
  96. return "python3";
  97. }
  98. private string GetConversionScriptPath()
  99. {
  100. var guids = AssetDatabase.FindAssets("convert_scene");
  101. if (guids.Length == 0)
  102. {
  103. UnityEngine.Debug.LogError("Conversion script 'convert_scene.py' not found.");
  104. return null;
  105. }
  106. var path = AssetDatabase.GUIDToAssetPath(guids[0]);
  107. return Path.Combine(ProjectRoot, path);
  108. }
  109. // Executes the Python conversion script for the selected assets.
  110. public void ExportAssets(AssetModel rootNode, string exportType)
  111. {
  112. var assetsToExport = new List<string>();
  113. CollectSelectedAssets(rootNode, assetsToExport);
  114. var pythonExecutable = FindPythonExecutable();
  115. var conversionScript = GetConversionScriptPath();
  116. if (string.IsNullOrEmpty(conversionScript)) return;
  117. var outputDirectory = Path.Combine(ProjectRoot, "Library", exportType);
  118. Directory.CreateDirectory(outputDirectory);
  119. EditorUtility.DisplayProgressBar("Exporting...", "Starting export process...", 0f);
  120. try
  121. {
  122. for (int i = 0; i < assetsToExport.Count; i++)
  123. {
  124. var relativeAssetPath = assetsToExport[i];
  125. var progress = (float)i / assetsToExport.Count;
  126. EditorUtility.DisplayProgressBar("Exporting...", $"Processing {Path.GetFileName(relativeAssetPath)}", progress);
  127. var absoluteAssetPath = Path.Combine(ProjectRoot, relativeAssetPath);
  128. var outputJsonPath = Path.Combine(outputDirectory, relativeAssetPath.Replace('/', Path.DirectorySeparatorChar) + ".json");
  129. var fileOutputDir = Path.GetDirectoryName(outputJsonPath);
  130. if (!Directory.Exists(fileOutputDir))
  131. {
  132. Directory.CreateDirectory(fileOutputDir);
  133. }
  134. var process = new Process
  135. {
  136. StartInfo = new ProcessStartInfo
  137. {
  138. FileName = pythonExecutable,
  139. Arguments = $"\"{conversionScript}\" \"{absoluteAssetPath}\" \"{outputJsonPath}\"",
  140. RedirectStandardOutput = true,
  141. RedirectStandardError = true,
  142. UseShellExecute = false,
  143. CreateNoWindow = true
  144. }
  145. };
  146. process.Start();
  147. string output = process.StandardOutput.ReadToEnd();
  148. string error = process.StandardError.ReadToEnd();
  149. process.WaitForExit();
  150. if (process.ExitCode != 0)
  151. {
  152. UnityEngine.Debug.LogError($"Error exporting {relativeAssetPath}: {error}");
  153. }
  154. else
  155. {
  156. UnityEngine.Debug.Log($"Successfully exported {relativeAssetPath}: {output}");
  157. }
  158. }
  159. }
  160. finally
  161. {
  162. EditorUtility.ClearProgressBar();
  163. }
  164. }
  165. // Recursively collects the paths of all selected assets.
  166. private void CollectSelectedAssets(AssetModel node, List<string> selectedPaths)
  167. {
  168. // If the node itself is a file and is selected, add it.
  169. if (node.Children.Count == 0 && node.IsSelected)
  170. {
  171. selectedPaths.Add(node.Path);
  172. }
  173. // Recurse into children
  174. foreach (var child in node.Children)
  175. {
  176. CollectSelectedAssets(child, selectedPaths);
  177. }
  178. }
  179. }
  180. }