init
This commit is contained in:
		
						commit
						001bb14f16
					
				
					 951 changed files with 270074 additions and 0 deletions
				
			
		
							
								
								
									
										334
									
								
								Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										334
									
								
								Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,334 @@ | |||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using UnityEditor.Experimental.SceneManagement; | ||||
| using UnityEditor.SceneManagement; | ||||
| using UnityEngine.AI; | ||||
| using UnityEngine; | ||||
| 
 | ||||
| namespace UnityEditor.AI | ||||
| { | ||||
|     public class NavMeshAssetManager : ScriptableSingleton<NavMeshAssetManager> | ||||
|     { | ||||
|         internal struct AsyncBakeOperation | ||||
|         { | ||||
|             public NavMeshSurface surface; | ||||
|             public NavMeshData bakeData; | ||||
|             public AsyncOperation bakeOperation; | ||||
|         } | ||||
| 
 | ||||
|         List<AsyncBakeOperation> m_BakeOperations = new List<AsyncBakeOperation>(); | ||||
|         internal List<AsyncBakeOperation> GetBakeOperations() { return m_BakeOperations; } | ||||
| 
 | ||||
|         struct SavedPrefabNavMeshData | ||||
|         { | ||||
|             public NavMeshSurface surface; | ||||
|             public NavMeshData navMeshData; | ||||
|         } | ||||
| 
 | ||||
|         List<SavedPrefabNavMeshData> m_PrefabNavMeshDataAssets = new List<SavedPrefabNavMeshData>(); | ||||
| 
 | ||||
|         static string GetAndEnsureTargetPath(NavMeshSurface surface) | ||||
|         { | ||||
|             // Create directory for the asset if it does not exist yet. | ||||
|             var activeScenePath = surface.gameObject.scene.path; | ||||
| 
 | ||||
|             var targetPath = "Assets"; | ||||
|             if (!string.IsNullOrEmpty(activeScenePath)) | ||||
|             { | ||||
|                 targetPath = Path.Combine(Path.GetDirectoryName(activeScenePath), Path.GetFileNameWithoutExtension(activeScenePath)); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 var prefabStage = PrefabStageUtility.GetPrefabStage(surface.gameObject); | ||||
|                 var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(surface.gameObject); | ||||
| 
 | ||||
|                 if (isPartOfPrefab) | ||||
|                 { | ||||
| #if UNITY_2020_1_OR_NEWER | ||||
|                     var assetPath = prefabStage.assetPath; | ||||
| #else | ||||
|                     var assetPath = prefabStage.prefabAssetPath; | ||||
| #endif | ||||
|                     if (!string.IsNullOrEmpty(assetPath)) | ||||
|                     { | ||||
|                         var prefabDirectoryName = Path.GetDirectoryName(assetPath); | ||||
|                         if (!string.IsNullOrEmpty(prefabDirectoryName)) | ||||
|                             targetPath = prefabDirectoryName; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if (!Directory.Exists(targetPath)) | ||||
|                 Directory.CreateDirectory(targetPath); | ||||
|             return targetPath; | ||||
|         } | ||||
| 
 | ||||
|         static void CreateNavMeshAsset(NavMeshSurface surface) | ||||
|         { | ||||
|             var targetPath = GetAndEnsureTargetPath(surface); | ||||
| 
 | ||||
|             var combinedAssetPath = Path.Combine(targetPath, "NavMesh-" + surface.name + ".asset"); | ||||
|             combinedAssetPath = AssetDatabase.GenerateUniqueAssetPath(combinedAssetPath); | ||||
|             AssetDatabase.CreateAsset(surface.navMeshData, combinedAssetPath); | ||||
|         } | ||||
| 
 | ||||
|         NavMeshData GetNavMeshAssetToDelete(NavMeshSurface navSurface) | ||||
|         { | ||||
|             if (PrefabUtility.IsPartOfPrefabInstance(navSurface) && !PrefabUtility.IsPartOfModelPrefab(navSurface)) | ||||
|             { | ||||
|                 // Don't allow deleting the asset belonging to the prefab parent | ||||
|                 var parentSurface = PrefabUtility.GetCorrespondingObjectFromSource(navSurface) as NavMeshSurface; | ||||
|                 if (parentSurface && navSurface.navMeshData == parentSurface.navMeshData) | ||||
|                     return null; | ||||
|             } | ||||
| 
 | ||||
|             // Do not delete the NavMeshData asset referenced from a prefab until the prefab is saved | ||||
|             var prefabStage = PrefabStageUtility.GetPrefabStage(navSurface.gameObject); | ||||
|             var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(navSurface.gameObject); | ||||
|             if (isPartOfPrefab && IsCurrentPrefabNavMeshDataStored(navSurface)) | ||||
|                 return null; | ||||
| 
 | ||||
|             return navSurface.navMeshData; | ||||
|         } | ||||
| 
 | ||||
|         void ClearSurface(NavMeshSurface navSurface) | ||||
|         { | ||||
|             var hasNavMeshData = navSurface.navMeshData != null; | ||||
|             StoreNavMeshDataIfInPrefab(navSurface); | ||||
| 
 | ||||
|             var assetToDelete = GetNavMeshAssetToDelete(navSurface); | ||||
|             navSurface.RemoveData(); | ||||
| 
 | ||||
|             if (hasNavMeshData) | ||||
|             { | ||||
|                 SetNavMeshData(navSurface, null); | ||||
|                 EditorSceneManager.MarkSceneDirty(navSurface.gameObject.scene); | ||||
|             } | ||||
| 
 | ||||
|             if (assetToDelete) | ||||
|                 AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(assetToDelete)); | ||||
|         } | ||||
| 
 | ||||
|         public void StartBakingSurfaces(UnityEngine.Object[] surfaces) | ||||
|         { | ||||
|             // Remove first to avoid double registration of the callback | ||||
|             EditorApplication.update -= UpdateAsyncBuildOperations; | ||||
|             EditorApplication.update += UpdateAsyncBuildOperations; | ||||
| 
 | ||||
|             foreach (NavMeshSurface surf in surfaces) | ||||
|             { | ||||
|                 StoreNavMeshDataIfInPrefab(surf); | ||||
| 
 | ||||
|                 var oper = new AsyncBakeOperation(); | ||||
| 
 | ||||
|                 oper.bakeData = InitializeBakeData(surf); | ||||
|                 oper.bakeOperation = surf.UpdateNavMesh(oper.bakeData); | ||||
|                 oper.surface = surf; | ||||
| 
 | ||||
|                 m_BakeOperations.Add(oper); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static NavMeshData InitializeBakeData(NavMeshSurface surface) | ||||
|         { | ||||
|             var emptySources = new List<NavMeshBuildSource>(); | ||||
|             var emptyBounds = new Bounds(); | ||||
|             return UnityEngine.AI.NavMeshBuilder.BuildNavMeshData(surface.GetBuildSettings(), emptySources, emptyBounds | ||||
|                 , surface.transform.position, surface.transform.rotation); | ||||
|         } | ||||
| 
 | ||||
|         void UpdateAsyncBuildOperations() | ||||
|         { | ||||
|             foreach (var oper in m_BakeOperations) | ||||
|             { | ||||
|                 if (oper.surface == null || oper.bakeOperation == null) | ||||
|                     continue; | ||||
| 
 | ||||
|                 if (oper.bakeOperation.isDone) | ||||
|                 { | ||||
|                     var surface = oper.surface; | ||||
|                     var delete = GetNavMeshAssetToDelete(surface); | ||||
|                     if (delete != null) | ||||
|                         AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(delete)); | ||||
| 
 | ||||
|                     surface.RemoveData(); | ||||
|                     SetNavMeshData(surface, oper.bakeData); | ||||
| 
 | ||||
|                     if (surface.isActiveAndEnabled) | ||||
|                         surface.AddData(); | ||||
|                     CreateNavMeshAsset(surface); | ||||
|                     EditorSceneManager.MarkSceneDirty(surface.gameObject.scene); | ||||
|                 } | ||||
|             } | ||||
|             m_BakeOperations.RemoveAll(o => o.bakeOperation == null || o.bakeOperation.isDone); | ||||
|             if (m_BakeOperations.Count == 0) | ||||
|                 EditorApplication.update -= UpdateAsyncBuildOperations; | ||||
|         } | ||||
| 
 | ||||
|         public bool IsSurfaceBaking(NavMeshSurface surface) | ||||
|         { | ||||
|             if (surface == null) | ||||
|                 return false; | ||||
| 
 | ||||
|             foreach (var oper in m_BakeOperations) | ||||
|             { | ||||
|                 if (oper.surface == null || oper.bakeOperation == null) | ||||
|                     continue; | ||||
| 
 | ||||
|                 if (oper.surface == surface) | ||||
|                     return true; | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         public void ClearSurfaces(UnityEngine.Object[] surfaces) | ||||
|         { | ||||
|             foreach (NavMeshSurface s in surfaces) | ||||
|                 ClearSurface(s); | ||||
|         } | ||||
| 
 | ||||
|         static void SetNavMeshData(NavMeshSurface navSurface, NavMeshData navMeshData) | ||||
|         { | ||||
|             var so = new SerializedObject(navSurface); | ||||
|             var navMeshDataProperty = so.FindProperty("m_NavMeshData"); | ||||
|             navMeshDataProperty.objectReferenceValue = navMeshData; | ||||
|             so.ApplyModifiedPropertiesWithoutUndo(); | ||||
|         } | ||||
| 
 | ||||
|         void StoreNavMeshDataIfInPrefab(NavMeshSurface surfaceToStore) | ||||
|         { | ||||
|             var prefabStage = PrefabStageUtility.GetPrefabStage(surfaceToStore.gameObject); | ||||
|             var isPartOfPrefab = prefabStage != null && prefabStage.IsPartOfPrefabContents(surfaceToStore.gameObject); | ||||
|             if (!isPartOfPrefab) | ||||
|                 return; | ||||
| 
 | ||||
|             // check if data has already been stored for this surface | ||||
|             foreach (var storedAssetInfo in m_PrefabNavMeshDataAssets) | ||||
|                 if (storedAssetInfo.surface == surfaceToStore) | ||||
|                     return; | ||||
| 
 | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabSaving += DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
| 
 | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|                 PrefabStage.prefabStageClosing += ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
| 
 | ||||
|             var isDataOwner = true; | ||||
|             if (PrefabUtility.IsPartOfPrefabInstance(surfaceToStore) && !PrefabUtility.IsPartOfModelPrefab(surfaceToStore)) | ||||
|             { | ||||
|                 var basePrefabSurface = PrefabUtility.GetCorrespondingObjectFromSource(surfaceToStore) as NavMeshSurface; | ||||
|                 isDataOwner = basePrefabSurface == null || surfaceToStore.navMeshData != basePrefabSurface.navMeshData; | ||||
|             } | ||||
|             m_PrefabNavMeshDataAssets.Add(new SavedPrefabNavMeshData { surface = surfaceToStore, navMeshData = isDataOwner ? surfaceToStore.navMeshData : null }); | ||||
|         } | ||||
| 
 | ||||
|         bool IsCurrentPrefabNavMeshDataStored(NavMeshSurface surface) | ||||
|         { | ||||
|             if (surface == null) | ||||
|                 return false; | ||||
| 
 | ||||
|             foreach (var storedAssetInfo in m_PrefabNavMeshDataAssets) | ||||
|             { | ||||
|                 if (storedAssetInfo.surface == surface) | ||||
|                     return storedAssetInfo.navMeshData == surface.navMeshData; | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         void DeleteStoredNavMeshDataAssetsForOwnedSurfaces(GameObject gameObjectInPrefab) | ||||
|         { | ||||
|             // Debug.LogFormat("DeleteStoredNavMeshDataAsset() when saving prefab {0}", gameObjectInPrefab.name); | ||||
| 
 | ||||
|             var surfaces = gameObjectInPrefab.GetComponentsInChildren<NavMeshSurface>(true); | ||||
|             foreach (var surface in surfaces) | ||||
|                 DeleteStoredPrefabNavMeshDataAsset(surface); | ||||
|         } | ||||
| 
 | ||||
|         void DeleteStoredPrefabNavMeshDataAsset(NavMeshSurface surface) | ||||
|         { | ||||
|             for (var i = m_PrefabNavMeshDataAssets.Count - 1; i >= 0; i--) | ||||
|             { | ||||
|                 var storedAssetInfo = m_PrefabNavMeshDataAssets[i]; | ||||
|                 if (storedAssetInfo.surface == surface) | ||||
|                 { | ||||
|                     var storedNavMeshData = storedAssetInfo.navMeshData; | ||||
|                     if (storedNavMeshData != null && storedNavMeshData != surface.navMeshData) | ||||
|                     { | ||||
|                         var assetPath = AssetDatabase.GetAssetPath(storedNavMeshData); | ||||
|                         AssetDatabase.DeleteAsset(assetPath); | ||||
|                     } | ||||
| 
 | ||||
|                     m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         void ForgetUnsavedNavMeshDataChanges(PrefabStage prefabStage) | ||||
|         { | ||||
|             // Debug.Log("On prefab closing - forget about this object's surfaces and stop caring about prefab saving"); | ||||
| 
 | ||||
|             if (prefabStage == null) | ||||
|                 return; | ||||
| 
 | ||||
|             var allSurfacesInPrefab = prefabStage.prefabContentsRoot.GetComponentsInChildren<NavMeshSurface>(true); | ||||
|             NavMeshSurface surfaceInPrefab = null; | ||||
|             var index = 0; | ||||
|             do | ||||
|             { | ||||
|                 if (allSurfacesInPrefab.Length > 0) | ||||
|                     surfaceInPrefab = allSurfacesInPrefab[index]; | ||||
| 
 | ||||
|                 for (var i = m_PrefabNavMeshDataAssets.Count - 1; i >= 0; i--) | ||||
|                 { | ||||
|                     var storedPrefabInfo = m_PrefabNavMeshDataAssets[i]; | ||||
|                     if (storedPrefabInfo.surface == null) | ||||
|                     { | ||||
|                         // Debug.LogFormat("A surface from the prefab got deleted after it has baked a new NavMesh but it hasn't saved it. Now the unsaved asset gets deleted. ({0})", storedPrefabInfo.navMeshData); | ||||
| 
 | ||||
|                         // surface got deleted, thus delete its initial NavMeshData asset | ||||
|                         if (storedPrefabInfo.navMeshData != null) | ||||
|                         { | ||||
|                             var assetPath = AssetDatabase.GetAssetPath(storedPrefabInfo.navMeshData); | ||||
|                             AssetDatabase.DeleteAsset(assetPath); | ||||
|                         } | ||||
| 
 | ||||
|                         m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     } | ||||
|                     else if (surfaceInPrefab != null && storedPrefabInfo.surface == surfaceInPrefab) | ||||
|                     { | ||||
|                         //Debug.LogFormat("The surface {0} from the prefab was storing the original navmesh data and now will be forgotten", surfaceInPrefab); | ||||
| 
 | ||||
|                         var baseSurface = PrefabUtility.GetCorrespondingObjectFromSource(surfaceInPrefab) as NavMeshSurface; | ||||
|                         if (baseSurface == null || surfaceInPrefab.navMeshData != baseSurface.navMeshData) | ||||
|                         { | ||||
|                             var assetPath = AssetDatabase.GetAssetPath(surfaceInPrefab.navMeshData); | ||||
|                             AssetDatabase.DeleteAsset(assetPath); | ||||
| 
 | ||||
|                             //Debug.LogFormat("The surface {0} from the prefab has baked new NavMeshData but did not save this change so the asset has been now deleted. ({1})", | ||||
|                             //    surfaceInPrefab, assetPath); | ||||
|                         } | ||||
| 
 | ||||
|                         m_PrefabNavMeshDataAssets.RemoveAt(i); | ||||
|                     } | ||||
|                 } | ||||
|             } while (++index < allSurfacesInPrefab.Length); | ||||
| 
 | ||||
|             if (m_PrefabNavMeshDataAssets.Count == 0) | ||||
|             { | ||||
|                 PrefabStage.prefabSaving -= DeleteStoredNavMeshDataAssetsForOwnedSurfaces; | ||||
|                 PrefabStage.prefabStageClosing -= ForgetUnsavedNavMeshDataChanges; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Gerard Gascón
						Gerard Gascón