/// <summary> /// Converts a NavMesh (or a NavMesh area) into a standard Unity mesh. This is later used /// to render the mesh on-screen using Unity's standard rendering tools. /// </summary> /// <param name="navMesh">Precalculated Nav Mesh Triangulation</param> /// <param name="vert_size">Size of vertex array</param> /// <param name="tri_size">Size of triangle array</param> /// <returns></returns> public static Mesh ConvertNavmeshToMesh(UnityEngine.AI.NavMeshTriangulation navMesh, int vert_size, int tri_size) { Mesh ret = new Mesh(); if (vert_size >= 65535) { Debug.LogError("Playable NavMesh too big (vertex count >= 65535)! Limit the size of the playable area using" + "Area Masks. For now no preview mesh will render."); return(ret); } Vector3[] vertices = new Vector3[vert_size]; for (int x = 0; x < vertices.Length; x++) { // Note: Unity navmesh is offset 0.05m from the ground. This pushes it down to 0 vertices[x] = navMesh.vertices[x]; } int[] triangles = new int[tri_size * 3]; for (int x = 0; x < triangles.Length; x++) { triangles[x] = navMesh.indices[x]; } ret.name = "Navmesh"; ret.vertices = vertices; ret.triangles = triangles; RemoveMeshDuplicates(ret); ret.RecalculateNormals(); ret.RecalculateBounds(); return(ret); }
static string GenerateNavToObj() { Vector3[] vs; int[] ids; string txt = ""; #if false NavMesh.Triangulate(out vs, out ids); #else UnityEngine.AI.NavMeshTriangulation tri = UnityEngine.AI.NavMesh.CalculateTriangulation(); vs = tri.vertices; ids = tri.indices; #endif foreach (Vector3 v in vs) { // EosHack : //txt += "v " + (v.x) + " " + (v.y) + " " + (v.z) + "\n"; txt += "v " + (v.x) + " " + (v.y + BaseOffset) + " " + (v.z) + "\n"; } for (int i = 0; i < ids.Length; i += 3) { txt += "f " + (ids[i] + 1) + " " + (ids[i + 1] + 1) + " " + (ids[i + 2] + 1) + "\n"; } return(txt); }
/// <summary> /// Write the specified value using the writer. /// </summary> /// <param name="value">Value.</param> /// <param name="writer">Writer.</param> public override void Write(object value, ISaveGameWriter writer) { UnityEngine.AI.NavMeshTriangulation navMeshTriangulation = (UnityEngine.AI.NavMeshTriangulation)value; writer.WriteProperty("vertices", navMeshTriangulation.vertices); writer.WriteProperty("indices", navMeshTriangulation.indices); writer.WriteProperty("areas", navMeshTriangulation.areas); }
public void Build() { if (fromUnityNavigation) { UnityEngine.AI.NavMeshTriangulation triangulatedNavMesh = UnityEngine.AI.NavMesh.CalculateTriangulation(); Mesh mesh = new Mesh(); mesh.name = "ExportedNavMesh"; mesh.vertices = triangulatedNavMesh.vertices; mesh.triangles = triangulatedNavMesh.indices; Mesh sourceMesh = mesh; if (sourceMesh == null) { return; } ScanInternal(sourceMesh.triangles, sourceMesh.vertices); } else { AstarPathEditor.MenuScan(); Pathfinding.RecastGraph target = AstarPath.active.graphs[0] as Pathfinding.RecastGraph; int[] triangles; Vector3[] vertices; GetRecastData(target, out triangles, out vertices); ScanInternal(triangles, vertices); } }
static void Export() { Debug.Log("NavMesh Export Start"); UnityEngine.AI.NavMeshTriangulation navMeshTriangulation = UnityEngine.AI.NavMesh.CalculateTriangulation(); //文件路径 string path = Application.dataPath + "/" /*+ "/AStar/obj/"*/ + SceneManager.GetActiveScene().name + ".obj"; //新建文件 StreamWriter streamWriter = new StreamWriter(path); //顶点 for (int i = 0; i < navMeshTriangulation.vertices.Length; i++) { streamWriter.WriteLine("v " + navMeshTriangulation.vertices[i].x + " " + navMeshTriangulation.vertices[i].y + " " + navMeshTriangulation.vertices[i].z); } streamWriter.WriteLine("g pPlane1"); //索引 for (int i = 0; i < navMeshTriangulation.indices.Length;) { streamWriter.WriteLine("f " + (navMeshTriangulation.indices[i] + 1) + " " + (navMeshTriangulation.indices[i + 1] + 1) + " " + (navMeshTriangulation.indices[i + 2] + 1)); i = i + 3; } streamWriter.Flush(); streamWriter.Close(); AssetDatabase.Refresh(); Debug.Log("NavMesh Export Success: " + path); }
public static void UpdateNavMesh(NavMeshRenderer mesh, SerializedObject serializedObject, ArcTeleportData teleportSettings) { UnityEngine.AI.NavMeshTriangulation tri = UnityEngine.AI.NavMesh.CalculateTriangulation(); int vert_size, tri_size; CullNavmeshTriangulation(ref tri, teleportSettings.NavAreaMask, out vert_size, out tri_size); Mesh m = ConvertNavmeshToMesh(tri, vert_size, tri_size); // Can't use SerializedProperties here because BorderPointSet doesn't derive from UnityEngine.Object mesh.SelectableMeshBorder = FindBorderEdges(m); teleportSettings.SelectableMesh = m; mesh.SelectableMesh = mesh.SelectableMesh; // Make sure that setter is called }
static int _m_CalculateTriangulation_xlua_st_(RealStatePtr L) { ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L); try { { UnityEngine.AI.NavMeshTriangulation __cl_gen_ret = UnityEngine.AI.NavMesh.CalculateTriangulation( ); translator.Push(L, __cl_gen_ret); return(1); } } catch (System.Exception __gen_e) { return(LuaAPI.luaL_error(L, "c# exception:" + __gen_e)); } }
/// <summary> /// Read the data using the reader. /// </summary> /// <param name="reader">Reader.</param> public override object Read(ISaveGameReader reader) { UnityEngine.AI.NavMeshTriangulation navMeshTriangulation = new UnityEngine.AI.NavMeshTriangulation(); foreach (string property in reader.Properties) { switch (property) { case "vertices": navMeshTriangulation.vertices = reader.ReadProperty <UnityEngine.Vector3[]> (); break; case "indices": navMeshTriangulation.indices = reader.ReadProperty <System.Int32[]> (); break; case "areas": navMeshTriangulation.areas = reader.ReadProperty <System.Int32[]> (); break; } } return(navMeshTriangulation); }
/// <summary> /// Modifies the given NavMesh so that only the Navigation areas are present in the mesh. This is done only /// by swapping, so that no new memory is allocated. /// </summary> /// <param name="navMesh">NavMesh data to modify</param> /// <param name="area">Area mask to include in returned mesh. Areas outside of this mask are culled.</param> /// <param name="vert_size">New size of navMesh.vertices</param> /// <param name="tri_size">New size of navMesh.areas and one third of the size of navMesh.indices</param> public static void CullNavmeshTriangulation(ref UnityEngine.AI.NavMeshTriangulation navMesh, int area, out int vert_size, out int tri_size) { // Step 1: re-order triangles so that valid areas are in front. Then determine tri_size. tri_size = navMesh.indices.Length / 3; for (int i = 0; i < tri_size; i++) { Vector3 p1 = navMesh.vertices[navMesh.indices[i * 3]]; Vector3 p2 = navMesh.vertices[navMesh.indices[i * 3 + 1]]; Vector3 p3 = navMesh.vertices[navMesh.indices[i * 3 + 2]]; Plane p = new Plane(p1, p2, p3); bool vertical = Mathf.Abs(Vector3.Dot(p.normal, Vector3.up)) > 0.99f; // If the current triangle isn't flat (normal is up) or if it doesn't match // with the provided mask, we should cull it. if (((1 << navMesh.areas[i]) & area) == 0 || !vertical) // If true this triangle should be culled. { // Swap area indices and triangle indices with the end of the array int t_ind = tri_size - 1; int t_area = navMesh.areas[t_ind]; navMesh.areas[t_ind] = navMesh.areas[i]; navMesh.areas[i] = t_area; for (int j = 0; j < 3; j++) { int t_v = navMesh.indices[t_ind * 3 + j]; navMesh.indices[t_ind * 3 + j] = navMesh.indices[i * 3 + j]; navMesh.indices[i * 3 + j] = t_v; } // Then reduce the size of the array, effectively cutting off the previous triangle tri_size--; // Stay on the same index so that we can check the triangle we just swapped. i--; } } // Step 2: Cull the vertices that aren't used. vert_size = 0; for (int i = 0; i < tri_size * 3; i++) { int prv = navMesh.indices[i]; if (prv >= vert_size) { int nxt = vert_size; // Bring the current vertex to the end of the "active" array by swapping it with what's currently there Vector3 t_v = navMesh.vertices[prv]; navMesh.vertices[prv] = navMesh.vertices[nxt]; navMesh.vertices[nxt] = t_v; // Now change around the values in the triangle indices to reflect the swap for (int j = i; j < tri_size * 3; j++) { if (navMesh.indices[j] == prv) { navMesh.indices[j] = nxt; } else if (navMesh.indices[j] == nxt) { navMesh.indices[j] = prv; } } // Increase the size of the vertex array to reflect the changes. vert_size++; } } }
public void PlaceProbes() { if (probeObject != null) { probeObject.transform.position = Vector3.zero; UnityEngine.AI.NavMeshTriangulation navMesh = UnityEngine.AI.NavMesh.CalculateTriangulation(); Vector3[] _pos = navMesh.vertices; // construct kd tree KdTree _kd = new KdTree(); KdTree.Entry[] _kdents = new KdTree.Entry[_pos.Length]; for (int i = 0; i < _kdents.Length; ++i) { _kdents[i] = new KdTree.Entry(_pos[i], i); } _kd.build(_kdents); List <ProbeGenPoint> probeGen = new List <ProbeGenPoint>(); foreach (Vector3 _pt in _pos) { probeGen.Add(new ProbeGenPoint(_pt, false)); } List <Vector3> mergedProbes = new List <Vector3>(); var _watch = new System.Diagnostics.Stopwatch(); _watch.Start(); var _queue = new KdTree.RQueue(); for (int i = 0; i < probeGen.Count; ++i) { ProbeGenPoint _pro = probeGen[i]; if (_pro.used) { continue; } float _mergedCnt = 1.0f; Vector3 _mergedPos = _pro.pos; var _neighbor = _kd.rquery(_queue, _pro.pos, mergeDistance); for (int n = 0; n < _neighbor.Length; ++n) { if (_neighbor[n] == i) { continue; } ProbeGenPoint _subject = probeGen[_neighbor[n]]; _subject.used = true; } if (_mergedCnt > 1.0f) { _mergedPos *= 1.0f / _mergedCnt; } for (int l = 0; l < layers; ++l) { mergedProbes.Add(_mergedPos + Vector3.up * (layerHeight * l)); } _pro.used = true; } _watch.Stop(); Log.I("merging completed in {0} ms", _watch.ElapsedMilliseconds); probeObject.probePositions = mergedProbes.ToArray(); } }
/// <summary> /// Execute when Update Navmesh button is clicked. /// </summary> private void OnNavmeshUpdateClick() { GameObject featureObject = FindFeatureOfType(typeof(ArcTeleportManager)); if (featureObject == null) { Debug.LogError("Add Arc Teleport before clicking Update Navmesh."); } else { ArcTeleportManager arcTeleportManager = featureObject.GetComponent <ArcTeleportManager>(); NavMeshRenderer mesh = featureObject.transform.Find("Navmesh").GetComponent <NavMeshRenderer>(); // Area Mask // string[] areas = GameObjectUtility.GetNavMeshAreaNames(); int[] area_index = new int[areas.Length]; int temp_mask = 0; for (int x = 0; x < areas.Length; x++) { area_index[x] = GameObjectUtility.GetNavMeshAreaFromName(areas[x]); temp_mask |= ((TeleportSettings.NavAreaMask >> area_index[x]) & 1) << x; } EditorGUI.BeginChangeCheck(); temp_mask = EditorGUILayout.MaskField("Area Mask", temp_mask, areas); if (EditorGUI.EndChangeCheck()) { TeleportSettings.NavAreaMask = 0; for (int x = 0; x < areas.Length; x++) { TeleportSettings.NavAreaMask |= (((temp_mask >> x) & 1) == 1 ? 0 : 1) << area_index[x]; } TeleportSettings.NavAreaMask = ~TeleportSettings.NavAreaMask; } // Sanity check for Null properties // bool HasMesh = (mesh.SelectableMesh != null && mesh.SelectableMesh.vertexCount != 0) || (mesh.SelectableMeshBorder != null && mesh.SelectableMeshBorder.Length != 0); bool MeshNull = mesh.SelectableMesh == null; bool BorderNull = mesh.SelectableMeshBorder == null; if (MeshNull || BorderNull) { string str = "Internal Error: "; if (MeshNull) { str += "Selectable Mesh == null. "; } if (BorderNull) { str += "Border point array == null. "; } str += "This may lead to strange behavior or serialization. Try updating the mesh or delete and recreate the Navmesh object. "; str += "If you are able to consistently get a Vive Nav Mesh object into this state, please submit a bug report."; EditorGUILayout.HelpBox(str, MessageType.Error); } UnityEngine.AI.NavMeshTriangulation tri = UnityEngine.AI.NavMesh.CalculateTriangulation(); int vert_size, tri_size; NavMeshHelper.CullNavmeshTriangulation(ref tri, TeleportSettings.NavAreaMask, out vert_size, out tri_size); Mesh m = NavMeshHelper.ConvertNavmeshToMesh(tri, vert_size, tri_size); // Can't use SerializedProperties here because BorderPointSet doesn't derive from UnityEngine.Object mesh.SelectableMeshBorder = NavMeshHelper.FindBorderEdges(m); TeleportSettings.SelectableMesh = m; mesh.SelectableMesh = mesh.SelectableMesh; TeleportSettings.Save(); } }