diff options
Diffstat (limited to 'Other/NavMeshTest/Assets/NavMeshComponents/Editor')
14 files changed, 1560 insertions, 0 deletions
diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs new file mode 100644 index 0000000..f372c61 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs @@ -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; + } + } + } +} diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs.meta b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs.meta new file mode 100644 index 0000000..d6567a8 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 178d8366aa1616849b91b66285c51454 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef new file mode 100644 index 0000000..3c9827e --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef @@ -0,0 +1,16 @@ +{ + "name": "NavMeshComponentsEditor", + "references": [ + "NavMeshComponents" + ], + "optionalUnityReferences": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [] +}
\ No newline at end of file diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef.meta b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef.meta new file mode 100644 index 0000000..75ca4c2 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 86c9d8e67265f41469be06142c397d17 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs new file mode 100644 index 0000000..0d3a676 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs @@ -0,0 +1,258 @@ +using UnityEngine; +using UnityEngine.AI; + +namespace UnityEditor.AI +{ + public static class NavMeshComponentsGUIUtility + { + public static void AreaPopup(string labelName, SerializedProperty areaProperty) + { + var areaIndex = -1; + var areaNames = GameObjectUtility.GetNavMeshAreaNames(); + for (var i = 0; i < areaNames.Length; i++) + { + var areaValue = GameObjectUtility.GetNavMeshAreaFromName(areaNames[i]); + if (areaValue == areaProperty.intValue) + areaIndex = i; + } + ArrayUtility.Add(ref areaNames, ""); + ArrayUtility.Add(ref areaNames, "Open Area Settings..."); + + var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); + EditorGUI.BeginProperty(rect, GUIContent.none, areaProperty); + + EditorGUI.BeginChangeCheck(); + areaIndex = EditorGUI.Popup(rect, labelName, areaIndex, areaNames); + + if (EditorGUI.EndChangeCheck()) + { + if (areaIndex >= 0 && areaIndex < areaNames.Length - 2) + areaProperty.intValue = GameObjectUtility.GetNavMeshAreaFromName(areaNames[areaIndex]); + else if (areaIndex == areaNames.Length - 1) + NavMeshEditorHelpers.OpenAreaSettings(); + } + + EditorGUI.EndProperty(); + } + + public static void AgentTypePopup(string labelName, SerializedProperty agentTypeID) + { + var index = -1; + var count = NavMesh.GetSettingsCount(); + var agentTypeNames = new string[count + 2]; + for (var i = 0; i < count; i++) + { + var id = NavMesh.GetSettingsByIndex(i).agentTypeID; + var name = NavMesh.GetSettingsNameFromID(id); + agentTypeNames[i] = name; + if (id == agentTypeID.intValue) + index = i; + } + agentTypeNames[count] = ""; + agentTypeNames[count + 1] = "Open Agent Settings..."; + + bool validAgentType = index != -1; + if (!validAgentType) + { + EditorGUILayout.HelpBox("Agent Type invalid.", MessageType.Warning); + } + + var rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); + EditorGUI.BeginProperty(rect, GUIContent.none, agentTypeID); + + EditorGUI.BeginChangeCheck(); + index = EditorGUI.Popup(rect, labelName, index, agentTypeNames); + if (EditorGUI.EndChangeCheck()) + { + if (index >= 0 && index < count) + { + var id = NavMesh.GetSettingsByIndex(index).agentTypeID; + agentTypeID.intValue = id; + } + else if (index == count + 1) + { + NavMeshEditorHelpers.OpenAgentSettings(-1); + } + } + + EditorGUI.EndProperty(); + } + + // Agent mask is a set (internally array/list) of agentTypeIDs. + // It is used to describe which agents modifiers apply to. + // There is a special case of "None" which is an empty array. + // There is a special case of "All" which is an array of length 1, and value of -1. + public static void AgentMaskPopup(string labelName, SerializedProperty agentMask) + { + // Contents of the dropdown box. + string popupContent = ""; + + if (agentMask.hasMultipleDifferentValues) + popupContent = "\u2014"; + else + popupContent = GetAgentMaskLabelName(agentMask); + + var content = new GUIContent(popupContent); + var popupRect = GUILayoutUtility.GetRect(content, EditorStyles.popup); + + EditorGUI.BeginProperty(popupRect, GUIContent.none, agentMask); + popupRect = EditorGUI.PrefixLabel(popupRect, 0, new GUIContent(labelName)); + bool pressed = GUI.Button(popupRect, content, EditorStyles.popup); + + if (pressed) + { + var show = !agentMask.hasMultipleDifferentValues; + var showNone = show && agentMask.arraySize == 0; + var showAll = show && IsAll(agentMask); + + var menu = new GenericMenu(); + menu.AddItem(new GUIContent("None"), showNone, SetAgentMaskNone, agentMask); + menu.AddItem(new GUIContent("All"), showAll, SetAgentMaskAll, agentMask); + menu.AddSeparator(""); + + var count = NavMesh.GetSettingsCount(); + for (var i = 0; i < count; i++) + { + var id = NavMesh.GetSettingsByIndex(i).agentTypeID; + var sname = NavMesh.GetSettingsNameFromID(id); + + var showSelected = show && AgentMaskHasSelectedAgentTypeID(agentMask, id); + var userData = new object[] { agentMask, id, !showSelected }; + menu.AddItem(new GUIContent(sname), showSelected, ToggleAgentMaskItem, userData); + } + + menu.DropDown(popupRect); + } + + EditorGUI.EndProperty(); + } + + public static GameObject CreateAndSelectGameObject(string suggestedName, GameObject parent) + { + var parentTransform = parent != null ? parent.transform : null; + var uniqueName = GameObjectUtility.GetUniqueNameForSibling(parentTransform, suggestedName); + var child = new GameObject(uniqueName); + + Undo.RegisterCreatedObjectUndo(child, "Create " + uniqueName); + if (parentTransform != null) + Undo.SetTransformParent(child.transform, parentTransform, "Parent " + uniqueName); + + Selection.activeGameObject = child; + + return child; + } + + static bool IsAll(SerializedProperty agentMask) + { + return agentMask.arraySize == 1 && agentMask.GetArrayElementAtIndex(0).intValue == -1; + } + + static void ToggleAgentMaskItem(object userData) + { + var args = (object[])userData; + var agentMask = (SerializedProperty)args[0]; + var agentTypeID = (int)args[1]; + var value = (bool)args[2]; + + ToggleAgentMaskItem(agentMask, agentTypeID, value); + } + + static void ToggleAgentMaskItem(SerializedProperty agentMask, int agentTypeID, bool value) + { + if (agentMask.hasMultipleDifferentValues) + { + agentMask.ClearArray(); + agentMask.serializedObject.ApplyModifiedProperties(); + } + + // Find which index this agent type is in the agentMask array. + int idx = -1; + for (var j = 0; j < agentMask.arraySize; j++) + { + var elem = agentMask.GetArrayElementAtIndex(j); + if (elem.intValue == agentTypeID) + idx = j; + } + + // Handle "All" special case. + if (IsAll(agentMask)) + { + agentMask.DeleteArrayElementAtIndex(0); + } + + // Toggle value. + if (value) + { + if (idx == -1) + { + agentMask.InsertArrayElementAtIndex(agentMask.arraySize); + agentMask.GetArrayElementAtIndex(agentMask.arraySize - 1).intValue = agentTypeID; + } + } + else + { + if (idx != -1) + { + agentMask.DeleteArrayElementAtIndex(idx); + } + } + + agentMask.serializedObject.ApplyModifiedProperties(); + } + + static void SetAgentMaskNone(object data) + { + var agentMask = (SerializedProperty)data; + agentMask.ClearArray(); + agentMask.serializedObject.ApplyModifiedProperties(); + } + + static void SetAgentMaskAll(object data) + { + var agentMask = (SerializedProperty)data; + agentMask.ClearArray(); + agentMask.InsertArrayElementAtIndex(0); + agentMask.GetArrayElementAtIndex(0).intValue = -1; + agentMask.serializedObject.ApplyModifiedProperties(); + } + + static string GetAgentMaskLabelName(SerializedProperty agentMask) + { + if (agentMask.arraySize == 0) + return "None"; + + if (IsAll(agentMask)) + return "All"; + + if (agentMask.arraySize <= 3) + { + var labelName = ""; + for (var j = 0; j < agentMask.arraySize; j++) + { + var elem = agentMask.GetArrayElementAtIndex(j); + var settingsName = NavMesh.GetSettingsNameFromID(elem.intValue); + if (string.IsNullOrEmpty(settingsName)) + continue; + + if (labelName.Length > 0) + labelName += ", "; + labelName += settingsName; + } + return labelName; + } + + return "Mixed..."; + } + + static bool AgentMaskHasSelectedAgentTypeID(SerializedProperty agentMask, int agentTypeID) + { + for (var j = 0; j < agentMask.arraySize; j++) + { + var elem = agentMask.GetArrayElementAtIndex(j); + if (elem.intValue == agentTypeID) + return true; + } + return false; + } + } +} diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs.meta b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs.meta new file mode 100644 index 0000000..2f29620 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 77fba670b979046f18d52d751e0d4659 +timeCreated: 1480524815 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs new file mode 100644 index 0000000..cc4941b --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs @@ -0,0 +1,279 @@ +using UnityEngine; +using UnityEngine.AI; + +namespace UnityEditor.AI +{ + [CanEditMultipleObjects] + [CustomEditor(typeof(NavMeshLink))] + class NavMeshLinkEditor : Editor + { + SerializedProperty m_AgentTypeID; + SerializedProperty m_Area; + SerializedProperty m_CostModifier; + SerializedProperty m_AutoUpdatePosition; + SerializedProperty m_Bidirectional; + SerializedProperty m_EndPoint; + SerializedProperty m_StartPoint; + SerializedProperty m_Width; + + static int s_SelectedID; + static int s_SelectedPoint = -1; + + static Color s_HandleColor = new Color(255f, 167f, 39f, 210f) / 255; + static Color s_HandleColorDisabled = new Color(255f * 0.75f, 167f * 0.75f, 39f * 0.75f, 100f) / 255; + + void OnEnable() + { + m_AgentTypeID = serializedObject.FindProperty("m_AgentTypeID"); + m_Area = serializedObject.FindProperty("m_Area"); + m_CostModifier = serializedObject.FindProperty("m_CostModifier"); + m_AutoUpdatePosition = serializedObject.FindProperty("m_AutoUpdatePosition"); + m_Bidirectional = serializedObject.FindProperty("m_Bidirectional"); + m_EndPoint = serializedObject.FindProperty("m_EndPoint"); + m_StartPoint = serializedObject.FindProperty("m_StartPoint"); + m_Width = serializedObject.FindProperty("m_Width"); + + s_SelectedID = 0; + s_SelectedPoint = -1; + + NavMeshVisualizationSettings.showNavigation++; + } + + void OnDisable() + { + NavMeshVisualizationSettings.showNavigation--; + } + + static Matrix4x4 UnscaledLocalToWorldMatrix(Transform t) + { + return Matrix4x4.TRS(t.position, t.rotation, Vector3.one); + } + + void AlignTransformToEndPoints(NavMeshLink navLink) + { + var mat = UnscaledLocalToWorldMatrix(navLink.transform); + + var worldStartPt = mat.MultiplyPoint(navLink.startPoint); + var worldEndPt = mat.MultiplyPoint(navLink.endPoint); + + var forward = worldEndPt - worldStartPt; + var up = navLink.transform.up; + + // Flatten + forward -= Vector3.Dot(up, forward) * up; + + var transform = navLink.transform; + transform.rotation = Quaternion.LookRotation(forward, up); + transform.position = (worldEndPt + worldStartPt) * 0.5f; + transform.localScale = Vector3.one; + + navLink.startPoint = transform.InverseTransformPoint(worldStartPt); + navLink.endPoint = transform.InverseTransformPoint(worldEndPt); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + NavMeshComponentsGUIUtility.AgentTypePopup("Agent Type", m_AgentTypeID); + EditorGUILayout.Space(); + + EditorGUILayout.PropertyField(m_StartPoint); + EditorGUILayout.PropertyField(m_EndPoint); + + GUILayout.BeginHorizontal(); + GUILayout.Space(EditorGUIUtility.labelWidth); + if (GUILayout.Button("Swap")) + { + foreach (NavMeshLink navLink in targets) + { + var tmp = navLink.startPoint; + navLink.startPoint = navLink.endPoint; + navLink.endPoint = tmp; + } + SceneView.RepaintAll(); + } + if (GUILayout.Button("Align Transform")) + { + foreach (NavMeshLink navLink in targets) + { + Undo.RecordObject(navLink.transform, "Align Transform to End Points"); + Undo.RecordObject(navLink, "Align Transform to End Points"); + AlignTransformToEndPoints(navLink); + } + SceneView.RepaintAll(); + } + GUILayout.EndHorizontal(); + EditorGUILayout.Space(); + + EditorGUILayout.PropertyField(m_Width); + EditorGUILayout.PropertyField(m_CostModifier); + EditorGUILayout.PropertyField(m_AutoUpdatePosition); + EditorGUILayout.PropertyField(m_Bidirectional); + + NavMeshComponentsGUIUtility.AreaPopup("Area Type", m_Area); + + serializedObject.ApplyModifiedProperties(); + + EditorGUILayout.Space(); + } + + static Vector3 CalcLinkRight(NavMeshLink navLink) + { + var dir = navLink.endPoint - navLink.startPoint; + return (new Vector3(-dir.z, 0.0f, dir.x)).normalized; + } + + static void DrawLink(NavMeshLink navLink) + { + var right = CalcLinkRight(navLink); + var rad = navLink.width * 0.5f; + + Gizmos.DrawLine(navLink.startPoint - right * rad, navLink.startPoint + right * rad); + Gizmos.DrawLine(navLink.endPoint - right * rad, navLink.endPoint + right * rad); + Gizmos.DrawLine(navLink.startPoint - right * rad, navLink.endPoint - right * rad); + Gizmos.DrawLine(navLink.startPoint + right * rad, navLink.endPoint + right * rad); + } + + [DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)] + static void RenderBoxGizmo(NavMeshLink navLink, GizmoType gizmoType) + { + if (!EditorApplication.isPlaying) + navLink.UpdateLink(); + + var color = s_HandleColor; + if (!navLink.enabled) + color = s_HandleColorDisabled; + + var oldColor = Gizmos.color; + var oldMatrix = Gizmos.matrix; + + Gizmos.matrix = UnscaledLocalToWorldMatrix(navLink.transform); + + Gizmos.color = color; + DrawLink(navLink); + + Gizmos.matrix = oldMatrix; + Gizmos.color = oldColor; + + Gizmos.DrawIcon(navLink.transform.position, "NavMeshLink Icon", true); + } + + [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)] + static void RenderBoxGizmoNotSelected(NavMeshLink navLink, GizmoType gizmoType) + { + if (NavMeshVisualizationSettings.showNavigation > 0) + { + var color = s_HandleColor; + if (!navLink.enabled) + color = s_HandleColorDisabled; + + var oldColor = Gizmos.color; + var oldMatrix = Gizmos.matrix; + + Gizmos.matrix = UnscaledLocalToWorldMatrix(navLink.transform); + + Gizmos.color = color; + DrawLink(navLink); + + Gizmos.matrix = oldMatrix; + Gizmos.color = oldColor; + } + + Gizmos.DrawIcon(navLink.transform.position, "NavMeshLink Icon", true); + } + + public void OnSceneGUI() + { + var navLink = (NavMeshLink)target; + if (!navLink.enabled) + return; + + var mat = UnscaledLocalToWorldMatrix(navLink.transform); + + var startPt = mat.MultiplyPoint(navLink.startPoint); + var endPt = mat.MultiplyPoint(navLink.endPoint); + var midPt = Vector3.Lerp(startPt, endPt, 0.35f); + var startSize = HandleUtility.GetHandleSize(startPt); + var endSize = HandleUtility.GetHandleSize(endPt); + var midSize = HandleUtility.GetHandleSize(midPt); + + var zup = Quaternion.FromToRotation(Vector3.forward, Vector3.up); + var right = mat.MultiplyVector(CalcLinkRight(navLink)); + + var oldColor = Handles.color; + Handles.color = s_HandleColor; + + Vector3 pos; + + if (navLink.GetInstanceID() == s_SelectedID && s_SelectedPoint == 0) + { + EditorGUI.BeginChangeCheck(); + Handles.CubeHandleCap(0, startPt, zup, 0.1f * startSize, Event.current.type); + pos = Handles.PositionHandle(startPt, navLink.transform.rotation); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(navLink, "Move link point"); + navLink.startPoint = mat.inverse.MultiplyPoint(pos); + } + } + else + { + if (Handles.Button(startPt, zup, 0.1f * startSize, 0.1f * startSize, Handles.CubeHandleCap)) + { + s_SelectedPoint = 0; + s_SelectedID = navLink.GetInstanceID(); + } + } + + if (navLink.GetInstanceID() == s_SelectedID && s_SelectedPoint == 1) + { + EditorGUI.BeginChangeCheck(); + Handles.CubeHandleCap(0, endPt, zup, 0.1f * startSize, Event.current.type); + pos = Handles.PositionHandle(endPt, navLink.transform.rotation); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(navLink, "Move link point"); + navLink.endPoint = mat.inverse.MultiplyPoint(pos); + } + } + else + { + if (Handles.Button(endPt, zup, 0.1f * endSize, 0.1f * endSize, Handles.CubeHandleCap)) + { + s_SelectedPoint = 1; + s_SelectedID = navLink.GetInstanceID(); + } + } + + EditorGUI.BeginChangeCheck(); + pos = Handles.Slider(midPt + right * navLink.width * 0.5f, right, midSize * 0.03f, Handles.DotHandleCap, 0); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(navLink, "Adjust link width"); + navLink.width = Mathf.Max(0.0f, 2.0f * Vector3.Dot(right, (pos - midPt))); + } + + EditorGUI.BeginChangeCheck(); + pos = Handles.Slider(midPt - right * navLink.width * 0.5f, -right, midSize * 0.03f, Handles.DotHandleCap, 0); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(navLink, "Adjust link width"); + navLink.width = Mathf.Max(0.0f, 2.0f * Vector3.Dot(-right, (pos - midPt))); + } + + Handles.color = oldColor; + } + + [MenuItem("GameObject/AI/NavMesh Link", false, 2002)] + static public void CreateNavMeshLink(MenuCommand menuCommand) + { + var parent = menuCommand.context as GameObject; + GameObject go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Link", parent); + go.AddComponent<NavMeshLink>(); + var view = SceneView.lastActiveSceneView; + if (view != null) + view.MoveToView(go.transform); + } + } +} diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs.meta b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs.meta new file mode 100644 index 0000000..7811af4 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: ece1e865d1ad84587872fe8580ab5a20 +timeCreated: 1477036743 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs new file mode 100644 index 0000000..a2c636c --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs @@ -0,0 +1,49 @@ +using UnityEngine.AI; + +namespace UnityEditor.AI +{ + [CanEditMultipleObjects] + [CustomEditor(typeof(NavMeshModifier))] + class NavMeshModifierEditor : Editor + { + SerializedProperty m_AffectedAgents; + SerializedProperty m_Area; + SerializedProperty m_IgnoreFromBuild; + SerializedProperty m_OverrideArea; + + void OnEnable() + { + m_AffectedAgents = serializedObject.FindProperty("m_AffectedAgents"); + m_Area = serializedObject.FindProperty("m_Area"); + m_IgnoreFromBuild = serializedObject.FindProperty("m_IgnoreFromBuild"); + m_OverrideArea = serializedObject.FindProperty("m_OverrideArea"); + + NavMeshVisualizationSettings.showNavigation++; + } + + void OnDisable() + { + NavMeshVisualizationSettings.showNavigation--; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_IgnoreFromBuild); + + EditorGUILayout.PropertyField(m_OverrideArea); + if (m_OverrideArea.boolValue) + { + EditorGUI.indentLevel++; + NavMeshComponentsGUIUtility.AreaPopup("Area Type", m_Area); + EditorGUI.indentLevel--; + } + + NavMeshComponentsGUIUtility.AgentMaskPopup("Affected Agents", m_AffectedAgents); + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs.meta b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs.meta new file mode 100644 index 0000000..4c36a17 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6fa04b4743e3947eba4d7b9e5832ea69 +timeCreated: 1477036742 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs new file mode 100644 index 0000000..c8f1778 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs @@ -0,0 +1,146 @@ +using UnityEditor.IMGUI.Controls; +using UnityEditorInternal; +using UnityEngine.AI; +using UnityEngine; + +namespace UnityEditor.AI +{ + [CanEditMultipleObjects] + [CustomEditor(typeof(NavMeshModifierVolume))] + class NavMeshModifierVolumeEditor : Editor + { + SerializedProperty m_AffectedAgents; + SerializedProperty m_Area; + SerializedProperty m_Center; + SerializedProperty m_Size; + + static Color s_HandleColor = new Color(187f, 138f, 240f, 210f) / 255; + static Color s_HandleColorDisabled = new Color(187f * 0.75f, 138f * 0.75f, 240f * 0.75f, 100f) / 255; + + BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle(); + + bool editingCollider + { + get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); } + } + + void OnEnable() + { + m_AffectedAgents = serializedObject.FindProperty("m_AffectedAgents"); + m_Area = serializedObject.FindProperty("m_Area"); + m_Center = serializedObject.FindProperty("m_Center"); + m_Size = serializedObject.FindProperty("m_Size"); + + NavMeshVisualizationSettings.showNavigation++; + } + + void OnDisable() + { + NavMeshVisualizationSettings.showNavigation--; + } + + Bounds GetBounds() + { + var navModifier = (NavMeshModifierVolume)target; + return new Bounds(navModifier.transform.position, navModifier.size); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume", + EditorGUIUtility.IconContent("EditCollider"), GetBounds, this); + + EditorGUILayout.PropertyField(m_Size); + EditorGUILayout.PropertyField(m_Center); + + NavMeshComponentsGUIUtility.AreaPopup("Area Type", m_Area); + NavMeshComponentsGUIUtility.AgentMaskPopup("Affected Agents", m_AffectedAgents); + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + } + + [DrawGizmo(GizmoType.Selected | GizmoType.Active)] + static void RenderBoxGizmo(NavMeshModifierVolume navModifier, GizmoType gizmoType) + { + var color = navModifier.enabled ? s_HandleColor : s_HandleColorDisabled; + var colorTrans = new Color(color.r * 0.75f, color.g * 0.75f, color.b * 0.75f, color.a * 0.15f); + + var oldColor = Gizmos.color; + var oldMatrix = Gizmos.matrix; + + Gizmos.matrix = navModifier.transform.localToWorldMatrix; + + Gizmos.color = colorTrans; + Gizmos.DrawCube(navModifier.center, navModifier.size); + + Gizmos.color = color; + Gizmos.DrawWireCube(navModifier.center, navModifier.size); + + Gizmos.matrix = oldMatrix; + Gizmos.color = oldColor; + + Gizmos.DrawIcon(navModifier.transform.position, "NavMeshModifierVolume Icon", true); + } + + [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)] + static void RenderBoxGizmoNotSelected(NavMeshModifierVolume navModifier, GizmoType gizmoType) + { + if (NavMeshVisualizationSettings.showNavigation > 0) + { + var color = navModifier.enabled ? s_HandleColor : s_HandleColorDisabled; + var oldColor = Gizmos.color; + var oldMatrix = Gizmos.matrix; + + Gizmos.matrix = navModifier.transform.localToWorldMatrix; + + Gizmos.color = color; + Gizmos.DrawWireCube(navModifier.center, navModifier.size); + + Gizmos.matrix = oldMatrix; + Gizmos.color = oldColor; + } + + Gizmos.DrawIcon(navModifier.transform.position, "NavMeshModifierVolume Icon", true); + } + + void OnSceneGUI() + { + if (!editingCollider) + return; + + var vol = (NavMeshModifierVolume)target; + var color = vol.enabled ? s_HandleColor : s_HandleColorDisabled; + using (new Handles.DrawingScope(color, vol.transform.localToWorldMatrix)) + { + m_BoundsHandle.center = vol.center; + m_BoundsHandle.size = vol.size; + + EditorGUI.BeginChangeCheck(); + m_BoundsHandle.DrawHandle(); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(vol, "Modified NavMesh Modifier Volume"); + Vector3 center = m_BoundsHandle.center; + Vector3 size = m_BoundsHandle.size; + vol.center = center; + vol.size = size; + EditorUtility.SetDirty(target); + } + } + } + + [MenuItem("GameObject/AI/NavMesh Modifier Volume", false, 2001)] + static public void CreateNavMeshModifierVolume(MenuCommand menuCommand) + { + var parent = menuCommand.context as GameObject; + var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Modifier Volume", parent); + go.AddComponent<NavMeshModifierVolume>(); + var view = SceneView.lastActiveSceneView; + if (view != null) + view.MoveToView(go.transform); + } + } +} diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs.meta b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs.meta new file mode 100644 index 0000000..f974eb4 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: c0f3bef2a67ae4e139538afec3e59b03 +timeCreated: 1477036743 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs new file mode 100644 index 0000000..c9f0068 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs @@ -0,0 +1,400 @@ +#define NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor.Experimental.SceneManagement; +using UnityEditor.IMGUI.Controls; +using UnityEditor.SceneManagement; +using UnityEditorInternal; +using UnityEngine.AI; +using UnityEngine; + +namespace UnityEditor.AI +{ + [CanEditMultipleObjects] + [CustomEditor(typeof(NavMeshSurface))] + class NavMeshSurfaceEditor : Editor + { + SerializedProperty m_AgentTypeID; + SerializedProperty m_BuildHeightMesh; + SerializedProperty m_Center; + SerializedProperty m_CollectObjects; + SerializedProperty m_DefaultArea; + SerializedProperty m_LayerMask; + SerializedProperty m_OverrideTileSize; + SerializedProperty m_OverrideVoxelSize; + SerializedProperty m_Size; + SerializedProperty m_TileSize; + SerializedProperty m_UseGeometry; + SerializedProperty m_VoxelSize; + +#if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF + SerializedProperty m_NavMeshData; +#endif + class Styles + { + public readonly GUIContent m_LayerMask = new GUIContent("Include Layers"); + + public readonly GUIContent m_ShowInputGeom = new GUIContent("Show Input Geom"); + public readonly GUIContent m_ShowVoxels = new GUIContent("Show Voxels"); + public readonly GUIContent m_ShowRegions = new GUIContent("Show Regions"); + public readonly GUIContent m_ShowRawContours = new GUIContent("Show Raw Contours"); + public readonly GUIContent m_ShowContours = new GUIContent("Show Contours"); + public readonly GUIContent m_ShowPolyMesh = new GUIContent("Show Poly Mesh"); + public readonly GUIContent m_ShowPolyMeshDetail = new GUIContent("Show Poly Mesh Detail"); + } + + static Styles s_Styles; + + static bool s_ShowDebugOptions; + + static Color s_HandleColor = new Color(127f, 214f, 244f, 100f) / 255; + static Color s_HandleColorSelected = new Color(127f, 214f, 244f, 210f) / 255; + static Color s_HandleColorDisabled = new Color(127f * 0.75f, 214f * 0.75f, 244f * 0.75f, 100f) / 255; + + BoxBoundsHandle m_BoundsHandle = new BoxBoundsHandle(); + + bool editingCollider + { + get { return EditMode.editMode == EditMode.SceneViewEditMode.Collider && EditMode.IsOwner(this); } + } + + void OnEnable() + { + m_AgentTypeID = serializedObject.FindProperty("m_AgentTypeID"); + m_BuildHeightMesh = serializedObject.FindProperty("m_BuildHeightMesh"); + m_Center = serializedObject.FindProperty("m_Center"); + m_CollectObjects = serializedObject.FindProperty("m_CollectObjects"); + m_DefaultArea = serializedObject.FindProperty("m_DefaultArea"); + m_LayerMask = serializedObject.FindProperty("m_LayerMask"); + m_OverrideTileSize = serializedObject.FindProperty("m_OverrideTileSize"); + m_OverrideVoxelSize = serializedObject.FindProperty("m_OverrideVoxelSize"); + m_Size = serializedObject.FindProperty("m_Size"); + m_TileSize = serializedObject.FindProperty("m_TileSize"); + m_UseGeometry = serializedObject.FindProperty("m_UseGeometry"); + m_VoxelSize = serializedObject.FindProperty("m_VoxelSize"); + +#if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF + m_NavMeshData = serializedObject.FindProperty("m_NavMeshData"); +#endif + NavMeshVisualizationSettings.showNavigation++; + } + + void OnDisable() + { + NavMeshVisualizationSettings.showNavigation--; + } + + Bounds GetBounds() + { + var navSurface = (NavMeshSurface)target; + return new Bounds(navSurface.transform.position, navSurface.size); + } + + public override void OnInspectorGUI() + { + if (s_Styles == null) + s_Styles = new Styles(); + + serializedObject.Update(); + + var bs = NavMesh.GetSettingsByID(m_AgentTypeID.intValue); + + if (bs.agentTypeID != -1) + { + // Draw image + const float diagramHeight = 80.0f; + Rect agentDiagramRect = EditorGUILayout.GetControlRect(false, diagramHeight); + NavMeshEditorHelpers.DrawAgentDiagram(agentDiagramRect, bs.agentRadius, bs.agentHeight, bs.agentClimb, bs.agentSlope); + } + NavMeshComponentsGUIUtility.AgentTypePopup("Agent Type", m_AgentTypeID); + + EditorGUILayout.Space(); + + EditorGUILayout.PropertyField(m_CollectObjects); + if ((CollectObjects)m_CollectObjects.enumValueIndex == CollectObjects.Volume) + { + EditorGUI.indentLevel++; + + EditMode.DoEditModeInspectorModeButton(EditMode.SceneViewEditMode.Collider, "Edit Volume", + EditorGUIUtility.IconContent("EditCollider"), GetBounds, this); + EditorGUILayout.PropertyField(m_Size); + EditorGUILayout.PropertyField(m_Center); + + EditorGUI.indentLevel--; + } + else + { + if (editingCollider) + EditMode.QuitEditMode(); + } + + EditorGUILayout.PropertyField(m_LayerMask, s_Styles.m_LayerMask); + EditorGUILayout.PropertyField(m_UseGeometry); + + EditorGUILayout.Space(); + + m_OverrideVoxelSize.isExpanded = EditorGUILayout.Foldout(m_OverrideVoxelSize.isExpanded, "Advanced"); + if (m_OverrideVoxelSize.isExpanded) + { + EditorGUI.indentLevel++; + + NavMeshComponentsGUIUtility.AreaPopup("Default Area", m_DefaultArea); + + // Override voxel size. + EditorGUILayout.PropertyField(m_OverrideVoxelSize); + + using (new EditorGUI.DisabledScope(!m_OverrideVoxelSize.boolValue || m_OverrideVoxelSize.hasMultipleDifferentValues)) + { + EditorGUI.indentLevel++; + + EditorGUILayout.PropertyField(m_VoxelSize); + + if (!m_OverrideVoxelSize.hasMultipleDifferentValues) + { + if (!m_AgentTypeID.hasMultipleDifferentValues) + { + float voxelsPerRadius = m_VoxelSize.floatValue > 0.0f ? (bs.agentRadius / m_VoxelSize.floatValue) : 0.0f; + EditorGUILayout.LabelField(" ", voxelsPerRadius.ToString("0.00") + " voxels per agent radius", EditorStyles.miniLabel); + } + if (m_OverrideVoxelSize.boolValue) + EditorGUILayout.HelpBox("Voxel size controls how accurately the navigation mesh is generated from the level geometry. A good voxel size is 2-4 voxels per agent radius. Making voxel size smaller will increase build time.", MessageType.None); + } + EditorGUI.indentLevel--; + } + + // Override tile size + EditorGUILayout.PropertyField(m_OverrideTileSize); + + using (new EditorGUI.DisabledScope(!m_OverrideTileSize.boolValue || m_OverrideTileSize.hasMultipleDifferentValues)) + { + EditorGUI.indentLevel++; + + EditorGUILayout.PropertyField(m_TileSize); + + if (!m_TileSize.hasMultipleDifferentValues && !m_VoxelSize.hasMultipleDifferentValues) + { + float tileWorldSize = m_TileSize.intValue * m_VoxelSize.floatValue; + EditorGUILayout.LabelField(" ", tileWorldSize.ToString("0.00") + " world units", EditorStyles.miniLabel); + } + + if (!m_OverrideTileSize.hasMultipleDifferentValues) + { + if (m_OverrideTileSize.boolValue) + EditorGUILayout.HelpBox("Tile size controls the how local the changes to the world are (rebuild or carve). Small tile size allows more local changes, while potentially generating more data overall.", MessageType.None); + } + EditorGUI.indentLevel--; + } + + + // Height mesh + using (new EditorGUI.DisabledScope(true)) + { + EditorGUILayout.PropertyField(m_BuildHeightMesh); + } + + EditorGUILayout.Space(); + EditorGUI.indentLevel--; + } + + EditorGUILayout.Space(); + + serializedObject.ApplyModifiedProperties(); + + var hadError = false; + var multipleTargets = targets.Length > 1; + foreach (NavMeshSurface navSurface in targets) + { + var settings = navSurface.GetBuildSettings(); + // Calculating bounds is potentially expensive when unbounded - so here we just use the center/size. + // It means the validation is not checking vertical voxel limit correctly when the surface is set to something else than "in volume". + var bounds = new Bounds(Vector3.zero, Vector3.zero); + if (navSurface.collectObjects == CollectObjects.Volume) + { + bounds = new Bounds(navSurface.center, navSurface.size); + } + + var errors = settings.ValidationReport(bounds); + if (errors.Length > 0) + { + if (multipleTargets) + EditorGUILayout.LabelField(navSurface.name); + foreach (var err in errors) + { + EditorGUILayout.HelpBox(err, MessageType.Warning); + } + GUILayout.BeginHorizontal(); + GUILayout.Space(EditorGUIUtility.labelWidth); + if (GUILayout.Button("Open Agent Settings...", EditorStyles.miniButton)) + NavMeshEditorHelpers.OpenAgentSettings(navSurface.agentTypeID); + GUILayout.EndHorizontal(); + hadError = true; + } + } + + if (hadError) + EditorGUILayout.Space(); + +#if NAVMESHCOMPONENTS_SHOW_NAVMESHDATA_REF + var nmdRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight); + + EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData); + var rectLabel = EditorGUI.PrefixLabel(nmdRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(m_NavMeshData.displayName)); + EditorGUI.EndProperty(); + + using (new EditorGUI.DisabledScope(true)) + { + EditorGUI.BeginProperty(nmdRect, GUIContent.none, m_NavMeshData); + EditorGUI.ObjectField(rectLabel, m_NavMeshData, GUIContent.none); + EditorGUI.EndProperty(); + } +#endif + using (new EditorGUI.DisabledScope(Application.isPlaying || m_AgentTypeID.intValue == -1)) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(EditorGUIUtility.labelWidth); + if (GUILayout.Button("Clear")) + { + NavMeshAssetManager.instance.ClearSurfaces(targets); + SceneView.RepaintAll(); + } + + if (GUILayout.Button("Bake")) + { + NavMeshAssetManager.instance.StartBakingSurfaces(targets); + } + + GUILayout.EndHorizontal(); + } + + // Show progress for the selected targets + var bakeOperations = NavMeshAssetManager.instance.GetBakeOperations(); + for (int i = bakeOperations.Count - 1; i >= 0; --i) + { + if (!targets.Contains(bakeOperations[i].surface)) + continue; + + var oper = 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 = bakeOperations[i].bakeData; + UnityEngine.AI.NavMeshBuilder.Cancel(bakeData); + bakeOperations.RemoveAt(i); + } + + EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(), p, "Baking: " + (int)(100 * p) + "%"); + if (p <= 1) + Repaint(); + + GUILayout.EndHorizontal(); + } + } + + [DrawGizmo(GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)] + static void RenderBoxGizmoSelected(NavMeshSurface navSurface, GizmoType gizmoType) + { + RenderBoxGizmo(navSurface, gizmoType, true); + } + + [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.Pickable)] + static void RenderBoxGizmoNotSelected(NavMeshSurface navSurface, GizmoType gizmoType) + { + if (NavMeshVisualizationSettings.showNavigation > 0) + RenderBoxGizmo(navSurface, gizmoType, false); + else + Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true); + } + + static void RenderBoxGizmo(NavMeshSurface navSurface, GizmoType gizmoType, bool selected) + { + var color = selected ? s_HandleColorSelected : s_HandleColor; + if (!navSurface.enabled) + color = s_HandleColorDisabled; + + var oldColor = Gizmos.color; + var oldMatrix = Gizmos.matrix; + + // Use the unscaled matrix for the NavMeshSurface + var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one); + Gizmos.matrix = localToWorld; + + if (navSurface.collectObjects == CollectObjects.Volume) + { + Gizmos.color = color; + Gizmos.DrawWireCube(navSurface.center, navSurface.size); + + if (selected && navSurface.enabled) + { + var colorTrans = new Color(color.r * 0.75f, color.g * 0.75f, color.b * 0.75f, color.a * 0.15f); + Gizmos.color = colorTrans; + Gizmos.DrawCube(navSurface.center, navSurface.size); + } + } + else + { + if (navSurface.navMeshData != null) + { + var bounds = navSurface.navMeshData.sourceBounds; + Gizmos.color = Color.grey; + Gizmos.DrawWireCube(bounds.center, bounds.size); + } + } + + Gizmos.matrix = oldMatrix; + Gizmos.color = oldColor; + + Gizmos.DrawIcon(navSurface.transform.position, "NavMeshSurface Icon", true); + } + + void OnSceneGUI() + { + if (!editingCollider) + return; + + var navSurface = (NavMeshSurface)target; + var color = navSurface.enabled ? s_HandleColor : s_HandleColorDisabled; + var localToWorld = Matrix4x4.TRS(navSurface.transform.position, navSurface.transform.rotation, Vector3.one); + using (new Handles.DrawingScope(color, localToWorld)) + { + m_BoundsHandle.center = navSurface.center; + m_BoundsHandle.size = navSurface.size; + + EditorGUI.BeginChangeCheck(); + m_BoundsHandle.DrawHandle(); + if (EditorGUI.EndChangeCheck()) + { + Undo.RecordObject(navSurface, "Modified NavMesh Surface"); + Vector3 center = m_BoundsHandle.center; + Vector3 size = m_BoundsHandle.size; + navSurface.center = center; + navSurface.size = size; + EditorUtility.SetDirty(target); + } + } + } + + [MenuItem("GameObject/AI/NavMesh Surface", false, 2000)] + public static void CreateNavMeshSurface(MenuCommand menuCommand) + { + var parent = menuCommand.context as GameObject; + var go = NavMeshComponentsGUIUtility.CreateAndSelectGameObject("NavMesh Surface", parent); + go.AddComponent<NavMeshSurface>(); + var view = SceneView.lastActiveSceneView; + if (view != null) + view.MoveToView(go.transform); + } + } +} diff --git a/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs.meta b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs.meta new file mode 100644 index 0000000..d9db5b9 --- /dev/null +++ b/Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1c32167dbf3314852b6006a288eb449b +timeCreated: 1476968447 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: |