void GenerateLinks(NavLinkGenerator gen)
        {
            var tri       = NavMesh.CalculateTriangulation();
            var edge_list = CreateEdges(tri);

            foreach (var edge in edge_list)
            {
                edge.ComputeDerivedData();
            }
            if (edge_list.Count() == 0)
            {
                return;
            }

            RemoveLinks();
            m_CreatedLinks.Clear();
            var parent = NavEdUtil.GetNamedRoot(k_LinkRootName);

            foreach (var edge in edge_list)
            {
                var mid  = edge.GetMidpoint();
                var fwd  = edge.m_Normal;
                var link = CreateNavLink(parent, gen, edge, mid, fwd);
                if (link != null)
                {
                    m_CreatedLinks.Add(link);
                }
            }
        }
        void RemoveLinks()
        {
            var nav_links = NavEdUtil.GetNamedRoot(k_LinkRootName).GetComponentsInChildren <NavMeshLink>();

            foreach (var link in nav_links)
            {
                GameObject.DestroyImmediate(link.gameObject);
            }
        }
        static void CreateNonWalkableVolumes(NavNonWalkableCollection collection)
        {
            if (collection.m_Volumes == null)
            {
                collection.m_Volumes = new List <NavMeshModifierVolume>();
            }

            ClearVolumes(collection);

            var surfaces  = NavEdUtil.GetAllInActiveScene <NavMeshSurface>();
            var colliders = surfaces
                            .SelectMany(s => (s as NavMeshSurface).GetComponentsInChildren <Collider>());
            var threshold_sqr = 1.5f * 1.5f;

            foreach (Collider c in colliders)
            {
                if (c.GetComponentInChildren <NavMeshModifierVolume>() != null)
                {
                    // Skip so we can move them out of m_Volumes to make them persistent.
                    Debug.Log($"[NavMesh] Skipping NonWalkable generation on {c.name} because it already has a NavMeshModifierVolume child.", c);
                    continue;
                }
                // Get unrotated bounds. Hopefully these are tighter.
                var t   = c.transform;
                var rot = t.rotation;
                var pos = t.position;
                t.position = Vector3.zero;
                t.rotation = Quaternion.identity;
                Physics.SyncTransforms();
                var b = c.bounds;
                t.rotation = rot;
                t.position = pos;

                if (b.size.sqrMagnitude > threshold_sqr)
                {
                    var obj = new GameObject("Block NavMesh - " + t.name);
                    obj.transform.SetParent(t);
                    obj.transform.SetPositionAndRotation(pos, rot);
                    obj.transform.localPosition = Vector3.zero;
                    obj.transform.localRotation = Quaternion.identity;

                    var vol = obj.AddComponent <NavMeshModifierVolume>();
                    vol.area = (int)NavMeshAreaIndex.NotWalkable;

                    var offset = 0.2f;
                    var size   = b.size;
                    size      -= Vector3.one * offset;
                    vol.size   = size;
                    vol.center = b.center + Vector3.down * offset;
                    Undo.RegisterCreatedObjectUndo(obj, "Create No Walk Volumes");
                    collection.m_Volumes.Add(vol);
                }
            }
            Undo.RecordObject(collection, "Create No Walk Volumes");
        }
        static NavNonWalkableCollection Get()
        {
            var root = NavEdUtil.GetNamedRoot(k_NoMeshVolumeRootName);

            if (root == null)
            {
                root = new GameObject(k_NoMeshVolumeRootName).transform;
            }
            var collection = root.GetComponent <NavNonWalkableCollection>();

            if (collection == null)
            {
                collection = root.gameObject.AddComponent <NavNonWalkableCollection>();
            }
            return(collection);
        }
        public override void OnInspectorGUI()
        {
            DrawDefaultInspector();

            var collection = target as NavNonWalkableCollection;

            using (new GUILayout.HorizontalScope())
            {
                if (GUILayout.Button("Clear Interior Volumes"))
                {
                    ClearVolumes(collection);
                }

                if (GUILayout.Button("Create Interior Volumes"))
                {
                    CreateNonWalkableVolumes(collection);
                }

                if (GUILayout.Button("Select NavMesh"))
                {
                    Selection.objects = NavEdUtil.GetAllInActiveScene <NavMeshSurface>();
                }
            }
        }
        public override void OnInspectorGUI()
        {
            DrawDefaultInspector();

            var gen = target as NavLinkGenerator;

            m_AttachDebugToLinks = EditorGUILayout.Toggle("Attach Debug To Links", m_AttachDebugToLinks);

            EditorGUILayout.HelpBox("Workflow: click these buttons from left to right. See tooltips for more info.", MessageType.None);

            using (new GUILayout.HorizontalScope())
            {
                if (GUILayout.Button(new GUIContent("Clear All", "Delete generated Interior Volumes, NavMesh, and NavMeshLinks.")))
                {
                    NavNonWalkableCollection_Editor.ClearNonWalkableVolumes();
                    NavMeshAssetManager.instance.ClearSurfaces(NavEdUtil.GetAllInActiveScene <NavMeshSurface>());
                    RemoveLinks();
                    SceneView.RepaintAll();
                    Debug.Log($"Removed NavMesh and NavMeshLinks from all NavMeshSurfaces.");
                }

                if (GUILayout.Button(new GUIContent("Create Interior Volumes", "Create NonWalkable volumes to prevent navmesh generation inside of solid objects.")))
                {
                    NavNonWalkableCollection_Editor.CreateNonWalkableVolumes();
                }

                if (GUILayout.Button(new GUIContent("Bake NavMesh", "Build navmesh for all NavMeshSurface.")))
                {
                    var surfaces = NavEdUtil.GetAllInActiveScene <NavMeshSurface>();
                    NavMeshAssetManager.instance.StartBakingSurfaces(surfaces);
                    Debug.Log($"Baked NavMesh for {surfaces.Length} NavMeshSurfaces.");
                }

                if (GUILayout.Button(new GUIContent("Bake Links", "Create NavMeshLinks along your navmesh edges.")))
                {
                    GenerateLinks(gen);
                    Debug.Log($"Baked NavMeshLinks.");
                }

                if (GUILayout.Button(new GUIContent("Select NavMesh", "Selecting the navmesh makes it draw in the Scene view so you can evaluate the quality of the mesh and the links.")))
                {
                    Selection.objects = NavEdUtil.GetAllInActiveScene <NavMeshSurface>();
                }
            }

            EditorGUILayout.Space();
            m_ShowCreatedLinks = EditorGUILayout.Foldout(m_ShowCreatedLinks, "Created Links", toggleOnLabelClick: true);
            if (m_ShowCreatedLinks)
            {
                foreach (var entry in m_CreatedLinks)
                {
                    using (new GUILayout.HorizontalScope())
                    {
                        EditorGUILayout.ObjectField(entry, typeof(NavMeshLink), allowSceneObjects: true);
                        using (new EditorGUI.DisabledScope(!m_AttachDebugToLinks))
                        {
                            if (GUILayout.Button("Draw"))
                            {
                                entry.GetComponent <NavLinkCreationReason>().Draw();

                                if (SceneView.lastActiveSceneView != null)
                                {
                                    // Prevent losing focus (we'd lose our state).
                                    ActiveEditorTracker.sharedTracker.isLocked = true;

                                    var activeGameObject = Selection.activeGameObject;
                                    activeGameObject = entry.gameObject;
                                    EditorGUIUtility.PingObject(activeGameObject);
                                    SceneView.lastActiveSceneView.FrameSelected();
                                    Selection.activeGameObject = activeGameObject;
                                }
                            }
                        }
                    }
                }
            }
        }