void InsertVertexAndCreateTriangle(MapNavMeshDefinition data) { if (SelectedVertices.Count() == 2) { var id = NewId(); ArrayUtility.Add(ref data.Vertices, new MapNavMeshVertex { Id = id, Position = Vector3.Lerp( data.GetVertex(SelectedVertices.First()).Position, data.GetVertex(SelectedVertices.Last()).Position, 0.5f ).RoundToInt() }); SelectedVertices.Add(id); CreateTriangle(data); SelectedVertices.Clear(); SelectedVertices.Add(id); Save(); } }
void AddVertex(MapNavMeshDefinition data) { ArrayUtility.Add(ref data.Vertices, new MapNavMeshVertex { Id = NewId(), Position = new Vector3() }); Save(); }
void CreateTriangle(MapNavMeshDefinition data) { if (SelectedVertices.Count() == 3) { ArrayUtility.Add(ref data.Triangles, new MapNavMeshTriangle { Id = NewId(), VertexIds = SelectedVertices.ToArray() }); Save(); } }
void DeleteVertices(MapNavMeshDefinition data) { if (SelectedVertices.Count > 0) { if (EditorUtility.DisplayDialog("Delete Vertices?", "Are You sure?", "Yes", "No")) { data.Vertices = data.Vertices.Where(x => SelectedVertices.Contains(x.Id) == false).ToArray(); data.Triangles = data.Triangles.Where(x => x.VertexIds.Any(y => SelectedVertices.Contains(y)) == false).ToArray(); Save(); SelectedVertices.Clear(); } } }
void DuplicateVertex(MapNavMeshDefinition data) { if (SelectedVertices.Count == 1) { var id = NewId(); ArrayUtility.Add(ref data.Vertices, new MapNavMeshVertex { Id = id, Position = data.GetVertex(SelectedVertices.First()).Position.RoundToInt() }); Save(); SelectedVertices.Clear(); SelectedVertices.Add(id); } }
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 } }