summaryrefslogtreecommitdiff
path: root/Other/NavMeshTest/Assets/NavMeshComponents/Editor
diff options
context:
space:
mode:
Diffstat (limited to 'Other/NavMeshTest/Assets/NavMeshComponents/Editor')
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs334
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshAssetManager.cs.meta11
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef16
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsEditor.asmdef.meta7
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs258
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshComponentsGUIUtility.cs.meta12
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs279
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshLinkEditor.cs.meta12
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs49
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierEditor.cs.meta12
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs146
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshModifierVolumeEditor.cs.meta12
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs400
-rw-r--r--Other/NavMeshTest/Assets/NavMeshComponents/Editor/NavMeshSurfaceEditor.cs.meta12
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: