using System.Collections.Generic; using System.IO; 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; public bool hasCreatedPartitions => scenePartitions != null && scenePartitions.Count > 0; public SortedList scenePartitions = null; public bool hasLoadedPartitions => loadedScenePartitions != null && loadedScenePartitions.Count > 0; public SortedList loadedScenePartitions = null; 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() { CreateScenePartitions(); SortedSet ids = new SortedSet(scenePartitions.Keys); LoadScenePartitions(ids); } private void CreateScenePartitions() { string dataPath = ScenePartitionUtils.GetDataPath(this); string[] files = Directory.GetFiles(dataPath); scenePartitions = new SortedList(); for (int i = 0; i < files.Length; i++) { ScenePartition scenePartition = new ScenePartition(files[i]); scenePartitions.Add(scenePartition.id, scenePartition); } } private void LoadScenePartitions(SortedSet partitionIds) { if (!hasCreatedPartitions) return; string scenePath = ScenePartitionUtils.GetScenePath(this); List data = new List(); loadedScenePartitions = new SortedList(); // Add the header. partitionIds.Add(0); // Create scene data. foreach (uint id in partitionIds) { ScenePartition p = scenePartitions[id]; data.AddRange(p.data); loadedScenePartitions.Add(p.id, p); } // Create scene. File.WriteAllLines(scenePath, data); AssetDatabase.Refresh(); } /// /// Convert scene to partitions and save them to disk. /// public void SaveAll() { if (!hasCreatedPartitions) return; DeleteLoadedPartitions(); // Delete the loaded partitions from disk so we can write the new ones. string dataPath = ScenePartitionUtils.GetDataPath(this); string scenePath = ScenePartitionUtils.GetScenePath(this); string[] data = File.ReadAllLines(scenePath); 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. { int idStartIndex = data[i].IndexOf(" &") + 2; // & is the start of the object id. int idLength = data[i].Length; File.WriteAllLines($"{dataPath}/{sceneName}-{data[i][idStartIndex..idLength]}.yaml", data[i..lastIndex]); lastIndex = i; } } File.WriteAllLines($"{dataPath}/{sceneName}.yaml", data[0..lastIndex]); LoadAll(); // Reload. } /// /// 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); scenePartitions.Clear(); AssetDatabase.Refresh(); } private void DeleteLoadedPartitions() { if (!hasCreatedPartitions) return; foreach (KeyValuePair scenePartition in scenePartitions) { if (!File.Exists(scenePartition.Value.filePath)) continue; File.Delete(scenePartition.Value.filePath); } scenePartitions.Clear(); } public void LoadPartitions(uint[] ids) { SortedSet partitionIds = new SortedSet(); for (int i = 0; i < ids.Length; i++) { SortedSet connections = ScenePartitionUtils.FindDeeplyLinkedObjects(scenePartitions, ids[i]); foreach (uint c in connections) { partitionIds.Add(c); } } LoadScenePartitions(partitionIds); } } }