2023-06-24 14:27:50 +02:00
using System.Collections.Generic ;
2023-06-24 16:43:10 +02:00
using System.IO ;
2023-06-26 01:03:14 +02:00
using System.Text.RegularExpressions ;
2023-06-24 14:27:50 +02:00
using UnityEditor ;
2023-06-24 16:43:10 +02:00
using UnityEditor.SceneManagement ;
2023-06-24 14:27:50 +02:00
using UnityEngine ;
2023-06-24 16:43:10 +02:00
using UnityEngine.SceneManagement ;
2023-06-24 14:27:50 +02:00
2023-06-24 16:43:10 +02:00
namespace VertexColor.ScenePartition.Editor
2023-06-24 14:27:50 +02:00
{
[CreateAssetMenu(fileName = "Scene", menuName = "Max/ScenePartitionSO")]
public class ScenePartitionSO : ScriptableObject
{
[field: SerializeField]
2023-06-26 01:03:14 +02:00
public SceneAsset SceneAsset { get ; private set ; } = null ;
public string SceneName = > SceneAsset = = null ? name : SceneAsset . name ;
2023-07-03 23:35:29 +02:00
public List < ulong > alwaysLoadIds = new List < ulong > { 0 , 1 , 2 , 3 , 4 } ;
2023-06-24 16:43:10 +02:00
2023-06-27 01:09:28 +02:00
public ScenePartitionData Data
2023-06-27 00:24:46 +02:00
{
get
{
2023-06-27 01:09:28 +02:00
// Load data from the ScriptableSingleton.
data ? ? = ScenePartitionSS . instance . GetScenePartitionData ( this ) ;
return data ;
2023-06-27 00:24:46 +02:00
}
}
2023-06-27 01:09:28 +02:00
private ScenePartitionData data = null ;
2023-06-27 00:24:46 +02:00
2023-06-24 16:43:10 +02:00
public void CreateScene ( )
{
2023-06-26 01:03:14 +02:00
if ( SceneAsset ! = null ) return ;
2023-06-24 16:43:10 +02:00
string scenePath = Path . Combine ( AssetDatabase . GetAssetPath ( this ) , $"../{this.name}.unity" ) ;
Scene scene = EditorSceneManager . NewScene ( NewSceneSetup . EmptyScene , NewSceneMode . Single ) ;
EditorSceneManager . SaveScene ( scene , scenePath ) ;
2023-06-27 00:24:46 +02:00
Save ( ) ;
2023-06-24 16:43:10 +02:00
2023-06-27 00:24:46 +02:00
AssetDatabase . SaveAssets ( ) ;
2023-06-24 16:43:10 +02:00
AssetDatabase . Refresh ( ) ;
2023-06-25 04:39:12 +02:00
2023-06-26 01:03:14 +02:00
SceneAsset = AssetDatabase . LoadAssetAtPath < SceneAsset > ( scenePath ) ;
2023-06-25 04:39:12 +02:00
AssetDatabase . SaveAssets ( ) ;
AssetDatabase . Refresh ( ) ;
2023-06-24 16:43:10 +02:00
}
2023-06-25 04:39:12 +02:00
/// <summary>
/// Load partitions from disk and construct the scene file.
/// </summary>
2023-06-24 16:43:10 +02:00
public void LoadAll ( )
2023-06-25 04:39:12 +02:00
{
CreateScenePartitions ( ) ;
2023-07-03 23:35:29 +02:00
SortedSet < ulong > ids = new SortedSet < ulong > ( Data . ScenePartitions . Keys ) ;
2023-06-25 04:39:12 +02:00
LoadScenePartitions ( ids ) ;
}
2023-06-27 01:09:28 +02:00
/// <summary>
2023-06-28 01:07:27 +02:00
/// Discard changes and reload loaded partitions.
2023-06-27 01:09:28 +02:00
/// </summary>
public void Reload ( )
{
if ( ! Data . HasLoadedPartitions ) return ;
2023-07-03 23:35:29 +02:00
LoadScenePartitions ( new SortedSet < ulong > ( Data . LoadedScenePartitions . Keys ) ) ;
2023-06-27 01:09:28 +02:00
}
2023-06-25 04:39:12 +02:00
private void CreateScenePartitions ( )
2023-06-24 16:43:10 +02:00
{
string dataPath = ScenePartitionUtils . GetDataPath ( this ) ;
2023-06-25 04:39:12 +02:00
string [ ] files = Directory . GetFiles ( dataPath ) ;
2023-06-27 01:09:28 +02:00
Data . ScenePartitions = new ScenePartitionSortedList ( ) ;
2023-06-25 04:39:12 +02:00
for ( int i = 0 ; i < files . Length ; i + + )
{
ScenePartition scenePartition = new ScenePartition ( files [ i ] ) ;
2023-06-27 01:09:28 +02:00
Data . ScenePartitions . Add ( scenePartition . id , scenePartition ) ;
2023-06-25 04:39:12 +02:00
}
}
2023-07-03 23:35:29 +02:00
private void LoadScenePartitions ( SortedSet < ulong > partitionIds )
2023-06-25 04:39:12 +02:00
{
2023-06-27 01:09:28 +02:00
if ( ! Data . HasCreatedPartitions ) return ;
2023-06-25 04:39:12 +02:00
2023-06-24 16:43:10 +02:00
string scenePath = ScenePartitionUtils . GetScenePath ( this ) ;
2023-06-27 01:09:28 +02:00
List < string > sceneData = new List < string > ( ) ;
2023-06-24 16:43:10 +02:00
2023-06-27 01:09:28 +02:00
Data . LoadedScenePartitions . Clear ( ) ;
2023-06-25 04:39:12 +02:00
2023-06-30 03:28:44 +02:00
// Add always load ids.
2023-07-03 23:35:29 +02:00
SortedSet < ulong > baseIds = GetAlwaysLoadIds ( ) ;
2023-06-30 03:28:44 +02:00
foreach ( var id in baseIds )
2023-06-25 15:51:54 +02:00
{
2023-06-30 03:28:44 +02:00
partitionIds . Add ( id ) ;
2023-06-25 15:51:54 +02:00
}
2023-06-25 04:39:12 +02:00
// Create scene data.
2023-07-03 23:35:29 +02:00
foreach ( ulong id in partitionIds )
2023-06-24 16:43:10 +02:00
{
2023-06-27 01:09:28 +02:00
ScenePartition p = Data . ScenePartitions [ id ] ;
sceneData . AddRange ( File . ReadAllLines ( p . filePath ) ) ;
Data . LoadedScenePartitions . Add ( p . id , p ) ;
2023-06-24 16:43:10 +02:00
}
2023-06-25 04:39:12 +02:00
// Create scene.
2023-06-27 01:09:28 +02:00
File . WriteAllLines ( scenePath , sceneData ) ;
2023-06-24 16:43:10 +02:00
AssetDatabase . Refresh ( ) ;
}
2023-06-25 04:39:12 +02:00
/// <summary>
/// Convert scene to partitions and save them to disk.
/// </summary>
2023-06-25 15:51:54 +02:00
public void Save ( )
2023-06-24 16:43:10 +02:00
{
2023-06-25 04:39:12 +02:00
DeleteLoadedPartitions ( ) ; // Delete the loaded partitions from disk so we can write the new ones.
2023-06-24 16:43:10 +02:00
2023-06-26 01:03:14 +02:00
string pattern = @"&(\d+)" ;
2023-06-24 16:43:10 +02:00
string dataPath = ScenePartitionUtils . GetDataPath ( this ) ;
string scenePath = ScenePartitionUtils . GetScenePath ( this ) ;
2023-06-26 01:03:14 +02:00
// Read the data from the scene file.
2023-06-27 01:09:28 +02:00
string [ ] sceneData = File . ReadAllLines ( scenePath ) ;
2023-06-24 16:43:10 +02:00
2023-06-26 01:03:14 +02:00
// Split it into blocks.
2023-06-27 01:09:28 +02:00
int lastIndex = sceneData . Length ;
for ( int i = sceneData . Length - 1 ; i > = 0 ; i - - )
2023-06-24 16:43:10 +02:00
{
2023-06-27 01:09:28 +02:00
if ( sceneData [ i ] . StartsWith ( "---" ) ) // --- is the start of a new yaml document.
2023-06-24 16:43:10 +02:00
{
2023-06-27 01:09:28 +02:00
Match match = Regex . Match ( sceneData [ i ] , pattern ) ;
2023-06-24 16:43:10 +02:00
2023-06-26 01:03:14 +02:00
if ( match . Success )
{
// Extract the file number
string id = match . Groups [ 1 ] . Value ;
// Write data to disk.
2023-06-27 01:09:28 +02:00
File . WriteAllLines ( $"{dataPath}/{SceneName}-{id}.yaml" , sceneData [ i . . lastIndex ] ) ;
2023-06-26 01:03:14 +02:00
}
2023-06-24 16:43:10 +02:00
lastIndex = i ;
}
}
2023-06-26 01:03:14 +02:00
// Write header to disk.
2023-06-27 01:09:28 +02:00
File . WriteAllLines ( $"{dataPath}/{SceneName}.yaml" , sceneData [ 0. . lastIndex ] ) ;
2023-06-24 16:43:10 +02:00
}
2023-06-25 04:39:12 +02:00
/// <summary>
/// Empty the scene and save it (so it has no changes in source control).
/// </summary>
2023-06-24 16:43:10 +02:00
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 - - )
{
2023-06-25 04:39:12 +02:00
DestroyImmediate ( gameObjects [ i ] ) ;
2023-06-24 16:43:10 +02:00
}
EditorSceneManager . SaveScene ( scene ) ;
2023-06-27 01:09:28 +02:00
Data . LoadedScenePartitions . Clear ( ) ;
2023-06-24 16:43:10 +02:00
AssetDatabase . Refresh ( ) ;
}
private void DeleteLoadedPartitions ( )
{
2023-06-27 01:09:28 +02:00
if ( ! Data . HasLoadedPartitions ) return ;
2023-06-24 16:43:10 +02:00
2023-07-03 23:35:29 +02:00
foreach ( KeyValuePair < ulong , ScenePartition > scenePartition in Data . LoadedScenePartitions )
2023-06-24 16:43:10 +02:00
{
2023-06-25 04:39:12 +02:00
if ( ! File . Exists ( scenePartition . Value . filePath ) ) continue ;
2023-06-24 16:43:10 +02:00
2023-06-25 04:39:12 +02:00
File . Delete ( scenePartition . Value . filePath ) ;
2023-06-24 16:43:10 +02:00
}
}
2023-06-24 14:27:50 +02:00
2023-07-03 23:35:29 +02:00
public void LoadPartitions ( ulong [ ] ids )
2023-06-24 14:27:50 +02:00
{
2023-07-03 23:35:29 +02:00
SortedSet < ulong > partitionIds = new SortedSet < ulong > ( ) ;
2023-06-25 04:39:12 +02:00
for ( int i = 0 ; i < ids . Length ; i + + )
2023-06-24 14:27:50 +02:00
{
2023-07-03 23:35:29 +02:00
SortedSet < ulong > connections = ScenePartitionUtils . FindDeeplyLinkedObjects ( Data . ScenePartitions , ids [ i ] ) ;
foreach ( ulong c in connections )
2023-06-25 04:39:12 +02:00
{
partitionIds . Add ( c ) ;
}
2023-06-24 14:27:50 +02:00
}
2023-06-25 04:39:12 +02:00
LoadScenePartitions ( partitionIds ) ;
2023-06-24 14:27:50 +02:00
}
2023-06-28 01:07:27 +02:00
2023-07-03 23:35:29 +02:00
private SortedSet < ulong > GetAlwaysLoadIds ( )
2023-06-28 01:07:27 +02:00
{
2023-07-03 23:35:29 +02:00
SortedSet < ulong > partitionIds = new SortedSet < ulong > ( ) ;
2023-06-30 03:28:44 +02:00
2023-07-03 23:35:29 +02:00
foreach ( ulong id in alwaysLoadIds )
2023-06-30 03:28:44 +02:00
{
2023-07-03 23:35:29 +02:00
SortedSet < ulong > connections = ScenePartitionUtils . FindDeeplyLinkedObjects ( Data . ScenePartitions , id ) ;
foreach ( ulong c in connections )
2023-06-30 03:28:44 +02:00
{
partitionIds . Add ( c ) ;
}
}
2023-06-28 01:07:27 +02:00
2023-06-30 03:28:44 +02:00
return partitionIds ;
}
public void GenerateSceneGridData ( )
{
2023-06-28 01:07:27 +02:00
LoadAll ( ) ;
2023-06-30 03:28:44 +02:00
if ( ! Data . HasCreatedPartitions ) return ;
2023-06-28 01:07:27 +02:00
Scene scene = EditorSceneManager . OpenScene ( ScenePartitionUtils . GetScenePath ( this ) , OpenSceneMode . Single ) ;
GameObject [ ] rootGameObjects = scene . GetRootGameObjects ( ) ;
Data . SceneGrid . Grid . Clear ( ) ;
2023-06-30 03:28:44 +02:00
//// Maybe later switch to getting the data from disk instead of loading the scene and then unloading it again.
//foreach (GameObject gameObject in rootGameObjects)
//{
// // https://forum.unity.com/threads/how-to-get-the-local-identifier-in-file-for-scene-objects.265686/
// PropertyInfo inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance);
// SerializedObject serializedObject = new SerializedObject(gameObject.transform);
// inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null);
// SerializedProperty localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile");
// long localId = localIdProp.longValue;
// if (localId == 0)
// {
// if (PrefabUtility.IsPartOfPrefabInstance(gameObject))
// {
// GlobalObjectId id = GlobalObjectId.GetGlobalObjectIdSlow(gameObject); // We could use this funtion for all objects. Might be a bit slower but is also simple.
// localId = long.Parse(id.targetPrefabId.ToString());
// Debug.Log($"{id.assetGUID} | {id.identifierType} | {id.targetObjectId} | {id.targetPrefabId}");
// if (id.targetObjectId == 0 && id.targetPrefabId == 0)
// {
// Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}");
// continue;
// }
// }
// else
// {
// Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}");
// continue;
// }
// }
// if (!Data.ScenePartitions.ContainsKey(localId))
// {
// Debug.LogWarning($"Could not find LocalIdentfierInFile for {gameObject.transform} {gameObject.name} {gameObject.transform.GetInstanceID()}");
// continue;
// }
// Data.SceneGrid.Insert(localId, gameObject.transform.position);
//}
GlobalObjectId [ ] ids = new GlobalObjectId [ rootGameObjects . Length ] ;
GlobalObjectId . GetGlobalObjectIdsSlow ( rootGameObjects , ids ) ;
2023-06-28 01:07:27 +02:00
2023-06-30 03:28:44 +02:00
for ( int i = 0 ; i < ids . Length ; i + + )
{
Debug . Log ( $"{ids[i].assetGUID} | {ids[i].identifierType} | {ids[i].targetObjectId} | {ids[i].targetPrefabId}" ) ;
2023-06-28 01:07:27 +02:00
2023-06-30 03:28:44 +02:00
if ( ids [ i ] . targetPrefabId = = 0 ) // 0 = no prefab.
2023-06-28 01:07:27 +02:00
{
2023-07-03 23:35:29 +02:00
Data . SceneGrid . Insert ( ids [ i ] . targetObjectId , rootGameObjects [ i ] . transform . position ) ;
2023-06-28 01:07:27 +02:00
}
else
{
2023-07-03 23:35:29 +02:00
Data . SceneGrid . Insert ( ids [ i ] . targetPrefabId , rootGameObjects [ i ] . transform . position ) ;
2023-06-28 01:07:27 +02:00
}
}
Unload ( ) ;
}
public void LoadCell ( int gridId )
{
2023-07-03 23:35:29 +02:00
if ( Data . SceneGrid . Grid . TryGetValue ( gridId , out GridList ids ) )
2023-06-28 01:07:27 +02:00
{
2023-07-03 23:35:29 +02:00
LoadPartitions ( ids . list . ToArray ( ) ) ;
2023-06-28 01:07:27 +02:00
}
}
2023-06-30 03:28:44 +02:00
public void ClearCache ( )
{
data = null ;
}
2023-06-24 14:27:50 +02:00
}
}