protected override void OnSceneViewGUI(SceneView sceneView) { if (!editMode) { base.OnSceneViewGUI(sceneView); } else if (navMeshTri.showNavMesh) { Handles.BeginGUI(); OnBoundsWindowGUI(sceneView); Handles.EndGUI(); if (!Application.isPlaying) { Event e = Event.current; if (e.type == EventType.keyUp && e.keyCode == KeyCode.Space) { GameObject[] selections = Selection.gameObjects; if (selections != null) { selections = Array.FindAll <GameObject>(selections, (GameObject each) => each.GetComponent <NavMeshTriangle>() != null); foreach (GameObject selection in selections) { if (selection != null) { NavMeshTriangle tri = selection.GetComponent <NavMeshTriangle>(); tri.state = (NavMeshTriangleState)(1 - (int)tri.state); } } } e.Use(); EditorUtility.SetDirty(navMeshTri); } } } }
private void UpdateTriangulation() { for (int i = mTriangles.Count - 1; i >= 0; i--) { if (mTriangles[i] != null) { GameObject.DestroyImmediate(mTriangles[i].gameObject); } } mTriangles.Clear(); GameObject[] gos = GameObject.FindObjectsOfType <GameObject>(); foreach (GameObject go in gos) { if (go.name.StartsWith("NavMeshTriangl")) { GameObject.DestroyImmediate(go); } } if (mTriangulation.indices != null) { for (int i = 0; i < mTriangulation.indices.Length; i += 3) { int i1 = mTriangulation.indices[i]; int i2 = mTriangulation.indices[i + 1]; int i3 = mTriangulation.indices[i + 2]; GameObject go = new GameObject(string.Format("NavMeshTriangle_{0}_{1}_{2}", i1, i2, i3)); GameObject.DontDestroyOnLoad(go); NavMeshTriangle tri = go.AddComponent <NavMeshTriangle>(); tri.indices = new int[] { i1, i2, i3 }; tri.Setup(new Vector3[] { mTriangulation.vertices[i1], mTriangulation.vertices[i2], mTriangulation.vertices[i3] }); mTriangles.Add(tri); } } }
protected override void ReadDataXML(XElement ele, ElderScrollsPlugin master) { XElement subEle; if (ele.TryPathTo("Triangles", false, out subEle)) { foreach (XElement e in subEle.Elements()) { var temp = new NavMeshTriangle(); temp.ReadXML(e, master); Triangles.Add(temp); } } }
private void AddTriangle(int[] tris, NavMeshTriangle triangle, List <NavMeshTriangle> burningTris, List <NavMeshTriangle> availableTris) { int index = burningTris.Count; burningTris.Add(triangle); foreach (int adjTriangleID in triangle.adjacentTriangles) { NavMeshTriangle adjTriangle = room.NavMeshGenerator.FindTriangleWithID(adjTriangleID); if (!burningTris.Contains(adjTriangle) && !availableTris.Contains(adjTriangle)) { availableTris.Add(adjTriangle); } } if (triangle.isConnectedToEntrance) { availableDoorTriangles.Add(triangle); } for (int j = 0; j < room.NavMeshGenerator.Vertexes.Length; j++) { if (room.NavMeshGenerator.Vertexes[j].Equals(triangle.vertex1)) { tris[index * 3] = j; break; } } for (int j = 0; j < room.NavMeshGenerator.Vertexes.Length; j++) { if (room.NavMeshGenerator.Vertexes[j].Equals(triangle.vertex2)) { tris[index * 3 + 1] = j; break; } } for (int j = 0; j < room.NavMeshGenerator.Vertexes.Length; j++) { if (room.NavMeshGenerator.Vertexes[j].Equals(triangle.vertex3)) { tris[index * 3 + 2] = j; break; } } }
protected override void ReadData(ESPReader reader) { using (MemoryStream stream = new MemoryStream(reader.ReadBytes(size))) using (ESPReader subReader = new ESPReader(stream, reader.Plugin)) { try { for (int i = 0; i < size / 16; i++) { var temp = new NavMeshTriangle(); temp.ReadBinary(subReader); Triangles.Add(temp); } } catch { return; } } }
Graph <NavMeshTriangle> NavMeshToGraph() { NavMeshTriangle[] navMeshTris = new NavMeshTriangle[mesh.triangles.Length / 3]; for (int i = 0; i < navMeshTris.Length; i++) { navMeshTris[i] = new NavMeshTriangle(); } triPairToEdgeMap = new Dictionary <Vector2Int, Vector2Int>(); Dictionary <Vector2Int, int> edgeMap = new Dictionary <Vector2Int, int>(); for (int i = 0; i < mesh.triangles.Length; i += 3) { int vi1 = mesh.triangles[i]; int vi2 = mesh.triangles[i + 1]; int vi3 = mesh.triangles[i + 2]; Vector2Int e1 = vi1 < vi2 ? new Vector2Int(vi1, vi2) : new Vector2Int(vi2, vi1); Vector2Int e2 = vi2 < vi3 ? new Vector2Int(vi2, vi3) : new Vector2Int(vi3, vi2); Vector2Int e3 = vi3 < vi1 ? new Vector2Int(vi3, vi1) : new Vector2Int(vi1, vi3); int tri = i / 3; navMeshTris[tri].navMeshIdx = tri; navMeshTris[tri].centroid = (mesh.vertices[vi1] + mesh.vertices[vi2] + mesh.vertices[vi3]) / 3; navMeshTris[tri].verts = new Vector3[] { mesh.vertices[vi1], mesh.vertices[vi2], mesh.vertices[vi3] }; Vector2 p0 = new Vector2(mesh.vertices[vi1].x, mesh.vertices[vi1].z); Vector2 p1 = new Vector2(mesh.vertices[vi2].x, mesh.vertices[vi2].z); Vector2 p2 = new Vector2(mesh.vertices[vi3].x, mesh.vertices[vi3].z); navMeshTris[tri].area = 0.5f * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y); if (edgeMap.ContainsKey(e1)) { navMeshTris[tri].AddNeighbor(edgeMap[e1], 1); navMeshTris[edgeMap[e1]].AddNeighbor(tri, 1); Vector2Int triPair = tri < edgeMap[e1] ? new Vector2Int(tri, edgeMap[e1]) : new Vector2Int(edgeMap[e1], tri); triPairToEdgeMap[triPair] = new Vector2Int(vi1, vi2); edgeMap.Remove(e1); } else { edgeMap[e1] = tri; } if (edgeMap.ContainsKey(e2)) { navMeshTris[tri].AddNeighbor(edgeMap[e2], 1); navMeshTris[edgeMap[e2]].AddNeighbor(tri, 1); Vector2Int triPair = tri < edgeMap[e2] ? new Vector2Int(tri, edgeMap[e2]) : new Vector2Int(edgeMap[e2], tri); triPairToEdgeMap[triPair] = new Vector2Int(vi2, vi3); edgeMap.Remove(e2); } else { edgeMap[e2] = tri; } if (edgeMap.ContainsKey(e3)) { navMeshTris[tri].AddNeighbor(edgeMap[e3], 1); navMeshTris[edgeMap[e3]].AddNeighbor(tri, 1); Vector2Int triPair = tri < edgeMap[e3] ? new Vector2Int(tri, edgeMap[e3]) : new Vector2Int(edgeMap[e3], tri); triPairToEdgeMap[triPair] = new Vector2Int(vi3, vi1); edgeMap.Remove(e3); } else { edgeMap[e3] = tri; } } //The keys that are left in edgeMap correspond to edges that are only part of one triangle. In other words, these edges make up //any boundaries of the navigation mesh foreach (var e in edgeMap.Keys) { int tri = edgeMap[e]; int vi1 = mesh.triangles[3 * tri]; int vi2 = mesh.triangles[3 * tri + 1]; int vi3 = mesh.triangles[3 * tri + 2]; int vi; if (vi1 != e[0] && vi1 != e[1]) { vi = vi1; } else if (vi2 != e[0] && vi2 != e[1]) { vi = vi2; } else { vi = vi3; } boundaryVerts.Add(e[0]); boundaryVerts.Add(e[1]); Vector2 ev0 = new Vector2(mesh.vertices[e[0]].x, mesh.vertices[e[0]].z); Vector2 ev1 = new Vector2(mesh.vertices[e[1]].x, mesh.vertices[e[1]].z); Vector2 v = new Vector2(mesh.vertices[vi].x, mesh.vertices[vi].z); Vector2 dir = (ev1 - ev0).normalized; Vector2 n = Vector2.Perpendicular(dir); if (Vector2.Dot(v - ev0, n) > 0) { navMeshTris[tri].boundaryEdges.Add(new BoundaryEdge(ev0, ev1, n)); } else { navMeshTris[tri].boundaryEdges.Add(new BoundaryEdge(ev1, ev0, -n)); } } return(new Graph <NavMeshTriangle>(navMeshTris)); }
public static NavMesh BakeNavMesh(MapData data, MapNavMeshDefinition navmeshDefinition) { try { FPMathUtils.LoadLookupTables(); var vs_array = navmeshDefinition.Vertices.ToArray(); var nav_vertices = vs_array.Map(x => new NavMeshVertex { Point = x.Position.ToFPVector2(), Neighbors = new Int32[0], Triangles = new Int32[0], Borders = new Int32[0] }); var nav_triangles = new NavMeshTriangle[0]; // TRIANGLES for (Int32 i = 0; i < navmeshDefinition.Triangles.Length; ++i) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Triangles", i, navmeshDefinition.Triangles.Length); var t = navmeshDefinition.Triangles[i]; var v0 = Array.FindIndex(vs_array, x => x.Id == t.VertexIds[0]); var v1 = Array.FindIndex(vs_array, x => x.Id == t.VertexIds[1]); var v2 = Array.FindIndex(vs_array, x => x.Id == t.VertexIds[2]); ArrayUtils.Add(ref nav_triangles, new NavMeshTriangle { Vertex0 = v0, Vertex1 = v1, Vertex2 = v2, Center = (nav_vertices[v0].Point + nav_vertices[v1].Point + nav_vertices[v2].Point) / FP._3 }); } // TRIANGLE GRID var nav_triangles_grid = new NavMeshTriangleNode[data.Asset.Settings.GridSize * data.Asset.Settings.GridSize]; //for (Int32 i = 0; i < nav_triangles_grid.Length; ++i) { // nav_triangles_grid[i] = i + 1; //} for (Int32 i = 0; i < nav_triangles.Length; ++i) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Triangle Grid", i, nav_triangles.Length); var v0 = nav_vertices[nav_triangles[i].Vertex0].Point; var v1 = nav_vertices[nav_triangles[i].Vertex1].Point; var v2 = nav_vertices[nav_triangles[i].Vertex2].Point; for (Int32 z = 0; z < data.Asset.Settings.GridSize; ++z) { for (Int32 x = 0; x < data.Asset.Settings.GridSize; ++x) { var bl = data.Asset.Settings.WorldOffset + new FPVector2(x * data.Asset.Settings.GridNodeSize, z * data.Asset.Settings.GridNodeSize); var br = bl + new FPVector2(data.Asset.Settings.GridNodeSize, 0); var ur = bl + new FPVector2(data.Asset.Settings.GridNodeSize, data.Asset.Settings.GridNodeSize); var ul = bl + new FPVector2(0, data.Asset.Settings.GridNodeSize); if ( // if any of the corners are inside the triangle FPCollision.TriangleContainsPoint(bl, v0, v1, v2) || FPCollision.TriangleContainsPoint(br, v0, v1, v2) || FPCollision.TriangleContainsPoint(ur, v0, v1, v2) || FPCollision.TriangleContainsPoint(ul, v0, v1, v2) || // BL => BR FPCollision.TriangleContainsPoint(v0, v1, bl, br) || FPCollision.TriangleContainsPoint(v1, v2, bl, br) || FPCollision.TriangleContainsPoint(v2, v0, bl, br) || // BR => UR FPCollision.TriangleContainsPoint(v0, v1, br, ur) || FPCollision.TriangleContainsPoint(v1, v2, br, ur) || FPCollision.TriangleContainsPoint(v2, v0, br, ur) || // UR => UL FPCollision.TriangleContainsPoint(v0, v1, ur, ul) || FPCollision.TriangleContainsPoint(v1, v2, ur, ul) || FPCollision.TriangleContainsPoint(v2, v0, ur, ul) || // UL => BL FPCollision.TriangleContainsPoint(v0, v1, ul, bl) || FPCollision.TriangleContainsPoint(v1, v2, ul, bl) || FPCollision.TriangleContainsPoint(v2, v0, ul, bl) ) { var idx = (z * data.Asset.Settings.GridSize) + x; if (nav_triangles_grid[idx].Triangles == null) { nav_triangles_grid[idx].Triangles = new Int32[0]; } // add triangle to this grid node ArrayUtils.Add(ref nav_triangles_grid[idx].Triangles, i); } } } } // VERTEX NEIGHBORS for (Int32 v = 0; v < nav_vertices.Length; ++v) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Vertex Neighbors", v, nav_vertices.Length); var triangles = new HashSet <Int32>(); var neighbors = new HashSet <Int32>(); for (Int32 t = 0; t < nav_triangles.Length; ++t) { var tr = nav_triangles[t]; if (tr.Vertex0 == v || tr.Vertex1 == v || tr.Vertex2 == v) { triangles.Add(t); neighbors.Add(tr.Vertex0); neighbors.Add(tr.Vertex1); neighbors.Add(tr.Vertex2); } } // remove itself from neighbors set neighbors.Remove(v); // nav_vertices[v].Triangles = triangles.OrderBy(x => x).ToArray(); nav_vertices[v].Neighbors = neighbors.ToArray(); } // BORDER EDGES for (Int32 t = 0; t < nav_triangles.Length; ++t) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Border Edges", t, nav_triangles.Length); var tr = nav_triangles[t]; if (IsBorderEdge(nav_triangles, t, tr.Vertex0, tr.Vertex1)) { ArrayUtils.Add(ref nav_vertices[tr.Vertex0].Borders, tr.Vertex1); ArrayUtils.Add(ref nav_vertices[tr.Vertex1].Borders, tr.Vertex0); } if (IsBorderEdge(nav_triangles, t, tr.Vertex1, tr.Vertex2)) { ArrayUtils.Add(ref nav_vertices[tr.Vertex1].Borders, tr.Vertex2); ArrayUtils.Add(ref nav_vertices[tr.Vertex2].Borders, tr.Vertex1); } if (IsBorderEdge(nav_triangles, t, tr.Vertex2, tr.Vertex0)) { ArrayUtils.Add(ref nav_vertices[tr.Vertex2].Borders, tr.Vertex0); ArrayUtils.Add(ref nav_vertices[tr.Vertex0].Borders, tr.Vertex2); } } // NORMALS var pt2 = FP._0_10 * FP._2; var pt3 = FP._0_10 * FP._3; for (Int32 i = 0; i < nav_vertices.Length; ++i) { Progress("Baking NavMesh '" + navmeshDefinition.name + "': Calculating Normals", i, nav_vertices.Length); var v = nav_vertices[i]; var tn = new FPVector2[3]; if (v.Borders != null) { // 0. preferred middle of borders var borders = v.Borders.Map(x => FPVector2.Normalize(nav_vertices[x].Point - v.Point)); if (borders.Length == 2) { tn[0] = FPVector2.Normalize(FPVector2.Lerp(borders[0], borders[1], FP._0_50)); } // 1. second preferred neighbor edge that is furthest away from borders if (v.Neighbors != null) { var neighbors = v.Neighbors.Where(x => !v.Borders.Contains(x)).Select(x => new Neighbor { Direction = FPVector2.Normalize(nav_vertices[x].Point - v.Point), Vertex = x } ).ToArray(); var max_dot = FP.MinValue; var max_neighbor = default(Neighbor); max_neighbor.Vertex = -1; for (Int32 n = 0; n < neighbors.Length; ++n) { var dot = FP._0; for (Int32 b = 0; b < borders.Length; ++b) { dot += FPVector2.Dot(borders[b], neighbors[n].Direction); } dot = FPMath.Abs(dot); if (dot > max_dot) { max_dot = dot; max_neighbor = neighbors[n]; } } if (max_neighbor.Vertex >= 0) { tn[1] = max_neighbor.Direction * pt2; } } } // 2. least preferred, avarage of triangle normals foreach (var tc in v.Triangles.Select(x => FPVector2.Normalize(TriangleCenter(nav_triangles[x], nav_vertices) - v.Point))) { tn[2] += tc; } tn[2] = FPVector2.Normalize(tn[2]); // find normal var failed = true; for (Int32 k = 0; failed && k < tn.Length; ++k) { if (tn[k] != FPVector2.Zero) { if (failed && TriangleContains(nav_triangles, nav_vertices, (v.Point + (tn[k] * pt3)))) { nav_vertices[i].Normal = FPVector2.Normalize(tn[k] * pt2); // we're done failed = false; } if (failed && TriangleContains(nav_triangles, nav_vertices, (v.Point + (-tn[k] * pt3)))) { nav_vertices[i].Normal = FPVector2.Normalize(-tn[k] * pt2); // we're done failed = false; } } } } // BORDER SET HashSet <Border> border_set = new HashSet <Border>(); for (Int32 v = 0; v < nav_vertices.Length; ++v) { Progress(navmeshDefinition.name + " Baking: Border Set", v, nav_vertices.Length); if (nav_vertices[v].Borders != null) { for (Int32 n = 0; n < nav_vertices[v].Borders.Length; ++n) { border_set.Add(new Border(v, nav_vertices[v].Borders[n], border_set.Count + 1)); } } } // BORDER GRID var nav_border_grid = new NavMeshBorderNode[data.Asset.Settings.GridSize * data.Asset.Settings.GridSize]; for (Int32 z = 0; z < data.Asset.Settings.GridSize; ++z) { for (Int32 x = 0; x < data.Asset.Settings.GridSize; ++x) { var idx = (z * data.Asset.Settings.GridSize) + x; Progress("Baking NavMesh '" + navmeshDefinition.name + "': Border Grid", idx, data.Asset.Settings.GridSize * data.Asset.Settings.GridSize); // set index key // nav_border_grid[idx].key = idx + 1; // var zn = (FP)z * data.Asset.Settings.GridNodeSize; var xn = (FP)x * data.Asset.Settings.GridNodeSize; FPVector2 bl = data.Asset.Settings.WorldOffset + new FPVector2(xn, zn); FPVector2 br = data.Asset.Settings.WorldOffset + new FPVector2(xn + data.Asset.Settings.GridNodeSize, zn); FPVector2 ur = data.Asset.Settings.WorldOffset + new FPVector2(xn + data.Asset.Settings.GridNodeSize, zn + data.Asset.Settings.GridNodeSize); FPVector2 ul = data.Asset.Settings.WorldOffset + new FPVector2(xn, zn + data.Asset.Settings.GridNodeSize); foreach (var b in border_set) { var p0 = nav_vertices[b.V0].Point; var p1 = nav_vertices[b.V1].Point; if ( FPCollision.LineIntersectsLine(p0, p1, bl, br) || FPCollision.LineIntersectsLine(p0, p1, br, ur) || FPCollision.LineIntersectsLine(p0, p1, ur, ul) || FPCollision.LineIntersectsLine(p0, p1, ul, bl) ) { if (nav_border_grid[idx].Borders == null) { nav_border_grid[idx].Borders = new NavMeshBorder[0]; } ArrayUtils.Add(ref nav_border_grid[idx].Borders, new NavMeshBorder { Key = b.Key, V0 = p0, V1 = p1, }); } } } } // TRIANGLE CENTER GRID var nav_triangles_center_grid = new Int32[data.Asset.Settings.GridSize * data.Asset.Settings.GridSize]; for (Int32 z = 0; z < data.Asset.Settings.GridSize; ++z) { for (Int32 x = 0; x < data.Asset.Settings.GridSize; ++x) { var idx = (z * data.Asset.Settings.GridSize) + x; Progress("Baking NavMesh '" + navmeshDefinition.name + "': Triangle Center Grid", idx, data.Asset.Settings.GridSize * data.Asset.Settings.GridSize); var zn = (FP)(z * data.Asset.Settings.GridNodeSize); var xn = (FP)(x * data.Asset.Settings.GridNodeSize); var g = data.Asset.Settings.WorldOffset + new FPVector2(xn, zn) + new FPVector2(FP.FromFloat_UNSAFE(data.Asset.Settings.GridNodeSize * 0.5f), FP.FromFloat_UNSAFE(data.Asset.Settings.GridNodeSize * 0.5f)); var d = FP.MaxValue; var t = -1; for (Int32 i = 0; i < nav_triangles.Length; ++i) { var c = nav_triangles[i].Center; if (FPVector2.DistanceSquared(g, c) < d) { d = FPVector2.DistanceSquared(g, c); t = i; } } Assert.Check(t >= 0); nav_triangles_center_grid[idx] = t; } } NavMesh navmesh; navmesh = new NavMesh(); navmesh.GridSize = data.Asset.Settings.GridSize; navmesh.GridNodeSize = data.Asset.Settings.GridNodeSize; navmesh.WorldOffset = data.Asset.Settings.WorldOffset; navmesh.Name = navmeshDefinition.name; navmesh.Vertices = nav_vertices; navmesh.BorderGrid = nav_border_grid; navmesh.Triangles = nav_triangles; navmesh.TrianglesGrid = nav_triangles_grid; navmesh.TrianglesCenterGrid = nav_triangles_center_grid; return(navmesh); } finally { #if UNITY_EDITOR EditorUtility.ClearProgressBar(); #endif } }
static FPVector2 TriangleCenter(NavMeshTriangle t, NavMeshVertex[] vs) { return((vs[t.Vertex0].Point + vs[t.Vertex1].Point + vs[t.Vertex2].Point) / FP._3); }