Use async baking to avoid blocking the editor.

Cancel is supported but undo is not - due to the asynchronous behaviour.
This commit is contained in:
Jakob Hunsballe 2017-03-20 19:15:16 +01:00
Родитель e9768b179a
Коммит e19cfb89fc
2 изменённых файлов: 108 добавлений и 20 удалений

Просмотреть файл

@ -39,6 +39,15 @@ namespace UnityEditor.AI
public readonly GUIContent m_ShowPolyMeshDetail = new GUIContent("Show Poly Mesh Detail");
}
struct AsyncBakeOperation
{
public NavMeshSurface surface;
public NavMeshData bakeData;
public AsyncOperation bakeOperation;
}
static List<AsyncBakeOperation> s_BakeOperations = new List<AsyncBakeOperation> ();
static Styles s_Styles;
static bool s_ShowDebugOptions;
@ -91,7 +100,7 @@ namespace UnityEditor.AI
return targetPath;
}
void CreateNavMeshAsset(NavMeshSurface surface)
static void CreateNavMeshAsset(NavMeshSurface surface)
{
var targetPath = GetAndEnsureTargetPath(surface);
@ -100,7 +109,7 @@ namespace UnityEditor.AI
AssetDatabase.CreateAsset(surface.bakedNavMeshData, combinedAssetPath);
}
NavMeshData GetNavMeshAssetToDelete(NavMeshSurface navSurface)
static NavMeshData GetNavMeshAssetToDelete(NavMeshSurface navSurface)
{
var prefabType = PrefabUtility.GetPrefabType(navSurface);
if (prefabType == PrefabType.PrefabInstance || prefabType == PrefabType.DisconnectedPrefabInstance)
@ -113,20 +122,6 @@ namespace UnityEditor.AI
return navSurface.bakedNavMeshData;
}
void BakeSurface(NavMeshSurface navSurface)
{
var assetToDelete = GetNavMeshAssetToDelete(navSurface);
navSurface.Bake();
EditorUtility.SetDirty(navSurface);
if (assetToDelete)
{
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(assetToDelete));
}
CreateNavMeshAsset(navSurface);
EditorSceneManager.MarkSceneDirty(navSurface.gameObject.scene);
}
void ClearSurface(NavMeshSurface navSurface)
{
var assetToDelete = GetNavMeshAssetToDelete(navSurface);
@ -296,12 +291,92 @@ namespace UnityEditor.AI
if (GUILayout.Button("Bake"))
{
foreach (NavMeshSurface navSurface in targets)
BakeSurface(navSurface);
SceneView.RepaintAll();
// Remove first to avoid double registration of the callback
EditorApplication.update -= UpdateAsyncBuildOperations;
EditorApplication.update += UpdateAsyncBuildOperations;
foreach (NavMeshSurface surf in targets)
{
var oper = new AsyncBakeOperation();
oper.bakeData = InitializeBakeData(surf);
oper.bakeOperation = surf.UpdateNavMesh(oper.bakeData);
oper.surface = surf;
s_BakeOperations.Add(oper);
}
}
GUILayout.EndHorizontal();
}
// Show progress for the selected targets
for (int i = s_BakeOperations.Count - 1; i >= 0; --i)
{
if (!targets.Contains(s_BakeOperations [i].surface))
continue;
var oper = s_BakeOperations[i].bakeOperation;
if (oper == null)
continue;
var p = oper.progress;
if (oper.isDone)
{
SceneView.RepaintAll();
continue;
}
GUILayout.BeginHorizontal();
if (GUILayout.Button("Cancel", EditorStyles.miniButton))
{
var bakeData = s_BakeOperations [i].bakeData;
UnityEngine.AI.NavMeshBuilder.Cancel(bakeData);
s_BakeOperations.RemoveAt(i);
}
EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(), p, "Baking: " + (int)(100 * p) + "%");
if (p <= 1)
Repaint();
GUILayout.EndHorizontal();
}
}
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);
}
static void UpdateAsyncBuildOperations()
{
foreach (var oper in s_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();
surface.bakedNavMeshData = oper.bakeData;
if (surface.isActiveAndEnabled)
surface.AddData();
CreateNavMeshAsset(surface);
EditorSceneManager.MarkSceneDirty(surface.gameObject.scene);
}
}
s_BakeOperations.RemoveAll(o => o.bakeOperation == null || o.bakeOperation.isDone);
if (s_BakeOperations.Count == 0)
EditorApplication.update -= UpdateAsyncBuildOperations;
}
[DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)]
@ -402,7 +477,7 @@ namespace UnityEditor.AI
}
[MenuItem("GameObject/AI/NavMesh Surface", false, 2000)]
static public void CreateNavMeshSurface(MenuCommand menuCommand)
public static void CreateNavMeshSurface(MenuCommand menuCommand)
{
var parent = menuCommand.context as GameObject;
var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Surface", parent);

Просмотреть файл

@ -161,6 +161,19 @@ namespace UnityEngine.AI
}
}
public AsyncOperation UpdateNavMesh(NavMeshData data)
{
var sources = CollectSources();
// Use unscaled bounds - this differs in behaviour from e.g. collider components.
// But is similar to reflection probe - and since navmesh data has no scaling support - it is the right choice here.
var sourcesBounds = new Bounds(m_Center, Abs(m_Size));
if (m_CollectObjects == CollectObjects.All || m_CollectObjects == CollectObjects.Children)
sourcesBounds = CalculateWorldBounds(sources);
return NavMeshBuilder.UpdateNavMeshDataAsync(data, GetBuildSettings(), sources, sourcesBounds);
}
static void Register(NavMeshSurface surface)
{
if (s_NavMeshSurfaces.Count == 0)