From b067522a9554bdac34238e124bc4d26c0565e5d4 Mon Sep 17 00:00:00 2001 From: max Date: Sun, 25 Jun 2023 04:39:12 +0200 Subject: [PATCH] ScenePartitionObject - Loading - Partial loading - Saving --- Editor/ScenePartition.cs | 70 ++++++++++++++++++++++ Editor/ScenePartition.cs.meta | 11 ++++ Editor/ScenePartitionSO.cs | 99 ++++++++++++++++++++++++-------- Editor/ScenePartitionSOEditor.cs | 55 ++++++++++++++++-- Editor/ScenePartitionUtils.cs | 28 +++++++++ 5 files changed, 236 insertions(+), 27 deletions(-) create mode 100644 Editor/ScenePartition.cs create mode 100644 Editor/ScenePartition.cs.meta diff --git a/Editor/ScenePartition.cs b/Editor/ScenePartition.cs new file mode 100644 index 0000000..7e89a78 --- /dev/null +++ b/Editor/ScenePartition.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace VertexColor.ScenePartition.Editor +{ + public class SceneIdComparer : IComparer + { + public int Compare(ScenePartition x, ScenePartition y) + { + if (x.id == y.id) + { + return 0; + } + if (x.id < y.id) + { + return -1; + } + + return 1; + } + } + + [System.Serializable] + public class ScenePartition + { + public uint id = 0; + public string filePath = null; + public string[] data = null; + public HashSet references = new HashSet(); + + public ScenePartition(string filePath) + { + this.filePath = filePath; + + data = File.ReadAllLines(filePath); + + if (data == null || data.Length == 0) return; + + { // Get id. + string pattern = @"&(\d+)"; + + // Find all matches using regex + Match match = Regex.Match(data[0], pattern); + + if (!match.Success) return; + if (!uint.TryParse(match.Groups[1].Value, out id)) return; + } + + { // Get references. + string pattern = @"fileID:\s(\d+)"; + + for (int i = 0; i < data.Length; i++) + { + // Find the match using regex + Match match = Regex.Match(data[i], pattern); + + if (!match.Success) continue; + + if (uint.TryParse(match.Groups[1].Value, out uint fileNumber)) + { + if (fileNumber == 0) continue; // 0 == nothing. + + references.Add(fileNumber); + } + } + } + } + } +} \ No newline at end of file diff --git a/Editor/ScenePartition.cs.meta b/Editor/ScenePartition.cs.meta new file mode 100644 index 0000000..4a71783 --- /dev/null +++ b/Editor/ScenePartition.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 002f02333c6d0b844bff6ab0266ad686 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/ScenePartitionSO.cs b/Editor/ScenePartitionSO.cs index 20bf97d..c92bf16 100644 --- a/Editor/ScenePartitionSO.cs +++ b/Editor/ScenePartitionSO.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; @@ -13,11 +12,13 @@ public class ScenePartitionSO : ScriptableObject { [field: SerializeField] public SceneAsset sceneAsset { get; private set; } = null; - public string sceneName { get; private set; } = ""; + public string sceneName => sceneAsset == null ? name : sceneAsset.name; - public bool hasLoadedPartitions => (loadedPartitions != null && loadedPartitions.Count > 0); - [SerializeField] - private List loadedPartitions = null; // Should not be serialized, this would be local data only. + 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() { @@ -29,34 +30,72 @@ public void CreateScene() 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(); - List files = Directory.GetFiles(dataPath).ToList(); - files.Sort(); - for (int i = 0; i < files.Count; i++) + loadedScenePartitions = new SortedList(); + + // Add the header. + partitionIds.Add(0); + + // Create scene data. + foreach (uint id in partitionIds) { - data.AddRange(File.ReadAllLines(files[i])); + ScenePartition p = scenePartitions[id]; + data.AddRange(p.data); + loadedScenePartitions.Add(p.id, p); } + // Create scene. File.WriteAllLines(scenePath, data); - loadedPartitions.AddRange(files); - AssetDatabase.Refresh(); } + /// + /// Convert scene to partitions and save them to disk. + /// public void SaveAll() { - if (!hasLoadedPartitions) return; + if (!hasCreatedPartitions) return; - DeleteLoadedPartitions(); + DeleteLoadedPartitions(); // Delete the loaded partitions from disk so we can write the new ones. string dataPath = ScenePartitionUtils.GetDataPath(this); string scenePath = ScenePartitionUtils.GetScenePath(this); @@ -78,8 +117,13 @@ public void SaveAll() } 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); @@ -91,36 +135,45 @@ public void Unload() for (int i = gameObjects.Length - 1; i >= 0; i--) { - Object.DestroyImmediate(gameObjects[i]); + DestroyImmediate(gameObjects[i]); } EditorSceneManager.SaveScene(scene); - loadedPartitions.Clear(); + scenePartitions.Clear(); AssetDatabase.Refresh(); } private void DeleteLoadedPartitions() { - if (loadedPartitions == null) return; + if (!hasCreatedPartitions) return; - for (int i = loadedPartitions.Count - 1; i >= 0; i--) + foreach (KeyValuePair scenePartition in scenePartitions) { - if (!File.Exists(loadedPartitions[i])) continue; + if (!File.Exists(scenePartition.Value.filePath)) continue; - File.Delete(loadedPartitions[i]); + File.Delete(scenePartition.Value.filePath); } - loadedPartitions.Clear(); + scenePartitions.Clear(); } - private void OnValidate() + public void LoadPartitions(uint[] ids) { - if (string.IsNullOrWhiteSpace(sceneName) && sceneAsset != null) + SortedSet partitionIds = new SortedSet(); + + for (int i = 0; i < ids.Length; i++) { - sceneName = sceneAsset.name; + SortedSet connections = ScenePartitionUtils.FindDeeplyLinkedObjects(scenePartitions, ids[i]); + + foreach (uint c in connections) + { + partitionIds.Add(c); + } } + + LoadScenePartitions(partitionIds); } } } \ No newline at end of file diff --git a/Editor/ScenePartitionSOEditor.cs b/Editor/ScenePartitionSOEditor.cs index fe6ff29..2118176 100644 --- a/Editor/ScenePartitionSOEditor.cs +++ b/Editor/ScenePartitionSOEditor.cs @@ -1,22 +1,26 @@ -using UnityEngine; using UnityEditor; +using UnityEngine; namespace VertexColor.ScenePartition.Editor { [CustomEditor(typeof(ScenePartitionSO))] public class ScenePartitionSOEditor : UnityEditor.Editor { + private int id = 0; + public override void OnInspectorGUI() { ScenePartitionSO scenePartitionSO = (target as ScenePartitionSO); + DrawDefaultInspector(); + serializedObject.Update(); //EditorGUILayout.PropertyField(sceneAssetProperty); serializedObject.ApplyModifiedProperties(); EditorGUILayout.Space(); - if(scenePartitionSO.sceneAsset == null) + if (scenePartitionSO.sceneAsset == null) { if (GUILayout.Button("Create Scene")) { @@ -30,7 +34,7 @@ public override void OnInspectorGUI() scenePartitionSO.LoadAll(); } - if (scenePartitionSO.hasLoadedPartitions) + if (scenePartitionSO.hasCreatedPartitions) { if (GUILayout.Button("Save All")) { @@ -43,17 +47,60 @@ public override void OnInspectorGUI() scenePartitionSO.Unload(); } + EditorGUILayout.Space(); + if (GUILayout.Button("Open Scene Data Folder")) { EditorUtility.RevealInFinder(ScenePartitionUtils.GetDataPath(scenePartitionSO)); } + + EditorGUILayout.Space(); + + id = EditorGUILayout.IntField("id", id); + + if (GUILayout.Button("Test Load")) + { + scenePartitionSO.LoadPartitions(new uint[1] { (uint)id }); + } } EditorGUILayout.Space(); using (new EditorGUI.DisabledGroupScope(true)) { - DrawDefaultInspector(); + if (scenePartitionSO.hasCreatedPartitions) + { + EditorGUILayout.LabelField($"scenePartitions"); + + foreach (System.Collections.Generic.KeyValuePair scenePartition in scenePartitionSO.scenePartitions) + { + EditorGUILayout.IntField((int)scenePartition.Value.id); + EditorGUI.indentLevel++; + foreach (var reference in scenePartition.Value.references) + { + EditorGUILayout.IntField((int)reference); + } + EditorGUI.indentLevel--; + } + } + + EditorGUILayout.Space(); + + if (scenePartitionSO.hasLoadedPartitions) + { + EditorGUILayout.LabelField($"loadedScenePartitions"); + + foreach (System.Collections.Generic.KeyValuePair scenePartition in scenePartitionSO.loadedScenePartitions) + { + EditorGUILayout.IntField((int)scenePartition.Value.id); + EditorGUI.indentLevel++; + foreach (var reference in scenePartition.Value.references) + { + EditorGUILayout.IntField((int)reference); + } + EditorGUI.indentLevel--; + } + } } } } diff --git a/Editor/ScenePartitionUtils.cs b/Editor/ScenePartitionUtils.cs index d4aac7d..af177ea 100644 --- a/Editor/ScenePartitionUtils.cs +++ b/Editor/ScenePartitionUtils.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; @@ -24,5 +25,32 @@ public static string GetScenePath(ScenePartitionSO scenePartitionSO) return scenePath; } + + public static SortedSet FindDeeplyLinkedObjects(SortedList scenePartitions, uint partitionId) + { + SortedSet linkedObjects = new SortedSet(); + FindDeeplyLinkedObjectsRecursive(scenePartitions, partitionId, linkedObjects); + return linkedObjects; + } + + private static void FindDeeplyLinkedObjectsRecursive(SortedList scenePartitions, uint partitionId, SortedSet linkedObjects) + { + if (linkedObjects.Contains(partitionId)) return; + if (!scenePartitions.TryGetValue(partitionId, out ScenePartition partition)) return; + + linkedObjects.Add(partitionId); + + foreach (uint reference in partition.references) + { + FindDeeplyLinkedObjectsRecursive(scenePartitions, reference, linkedObjects); + if (scenePartitions.TryGetValue(reference, out ScenePartition referencedPartition)) + { + foreach (uint subReference in referencedPartition.references) + { + FindDeeplyLinkedObjectsRecursive(scenePartitions, subReference, linkedObjects); + } + } + } + } } } \ No newline at end of file