using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; namespace VertexColor.ScenePartition.Editor { [CreateAssetMenu(fileName = "Scene", menuName = "Max/ScenePartitionSO")] public class ScenePartitionSO : ScriptableObject { [field: SerializeField] public SceneAsset SceneAsset { get; private set; } = null; public string SceneName => SceneAsset == null ? name : SceneAsset.name; [SerializeField] private ScenePartitionDataSO scenePartitionData = null; public List alwaysLoadIds = new List { 0, 1, 2, 3, 4 }; public void CreateScene() { if (SceneAsset != null) return; string scenePath = Path.Combine(AssetDatabase.GetAssetPath(this), $"../{this.name}.unity"); Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); EditorSceneManager.SaveScene(scene, scenePath); AssetDatabase.Refresh(); SceneAsset = AssetDatabase.LoadAssetAtPath(scenePath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } /// /// Load partitions from disk and construct the scene file. /// public void LoadAll() { AssetDatabase.StartAssetEditing(); CreateScenePartitions(); SortedSet ids = new SortedSet(scenePartitionData.ScenePartitions.Keys); LoadScenePartitions(ids); AssetDatabase.StopAssetEditing(); } private void CreateScenePartitions() { string dataPath = ScenePartitionUtils.GetDataPath(this); string[] files = Directory.GetFiles(dataPath); scenePartitionData.ScenePartitions = new ScenePartitionSortedList(); for (int i = 0; i < files.Length; i++) { ScenePartition scenePartition = new ScenePartition(files[i]); scenePartitionData.ScenePartitions.Add(scenePartition.id, scenePartition); } } private void LoadScenePartitions(SortedSet partitionIds) { if (!scenePartitionData.HasCreatedPartitions) return; string scenePath = ScenePartitionUtils.GetScenePath(this); List data = new List(); scenePartitionData.LoadedScenePartitions.Clear(); // Add default ids. for (int i = 0; i < alwaysLoadIds.Count; i++) { if (scenePartitionData.ScenePartitions.ContainsKey(alwaysLoadIds[i])) { partitionIds.Add(alwaysLoadIds[i]); } } // Create scene data. foreach (uint id in partitionIds) { ScenePartition p = scenePartitionData.ScenePartitions[id]; data.AddRange(File.ReadAllLines(p.filePath)); scenePartitionData.LoadedScenePartitions.Add(p.id, p); } // Create scene. File.WriteAllLines(scenePath, data); AssetDatabase.Refresh(); } /// /// Convert scene to partitions and save them to disk. /// public void Save() { if (!scenePartitionData.HasCreatedPartitions) return; if (!scenePartitionData.HasLoadedPartitions) return; DeleteLoadedPartitions(); // Delete the loaded partitions from disk so we can write the new ones. string pattern = @"&(\d+)"; string dataPath = ScenePartitionUtils.GetDataPath(this); string scenePath = ScenePartitionUtils.GetScenePath(this); // Read the data from the scene file. string[] data = File.ReadAllLines(scenePath); // Split it into blocks. int lastIndex = data.Length; for (int i = data.Length - 1; i >= 0; i--) { if (data[i].StartsWith("---")) // --- is the start of a new yaml document. { Match match = Regex.Match(data[i], pattern); if (match.Success) { // Extract the file number string id = match.Groups[1].Value; // Write data to disk. File.WriteAllLines($"{dataPath}/{SceneName}-{id}.yaml", data[i..lastIndex]); } lastIndex = i; } } // Write header to disk. File.WriteAllLines($"{dataPath}/{SceneName}.yaml", data[0..lastIndex]); } /// /// Empty the scene and save it (so it has no changes in source control). /// public void Unload() { string dataPath = ScenePartitionUtils.GetDataPath(this); string scenePath = ScenePartitionUtils.GetScenePath(this); Scene scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single); GameObject[] gameObjects = scene.GetRootGameObjects(); for (int i = gameObjects.Length - 1; i >= 0; i--) { DestroyImmediate(gameObjects[i]); } EditorSceneManager.SaveScene(scene); scenePartitionData.LoadedScenePartitions.Clear(); AssetDatabase.Refresh(); } private void DeleteLoadedPartitions() { if (!scenePartitionData.HasCreatedPartitions) return; foreach (KeyValuePair scenePartition in scenePartitionData.LoadedScenePartitions) { if (!File.Exists(scenePartition.Value.filePath)) continue; File.Delete(scenePartition.Value.filePath); } } public void LoadPartitions(uint[] ids) { SortedSet partitionIds = new SortedSet(); for (int i = 0; i < ids.Length; i++) { SortedSet connections = ScenePartitionUtils.FindDeeplyLinkedObjects(scenePartitionData.ScenePartitions, ids[i]); foreach (uint c in connections) { partitionIds.Add(c); } } LoadScenePartitions(partitionIds); } } }