public static void BakeMapData(MapData data, Boolean inEditor) { FPMathUtils.LoadLookupTables(); if (inEditor == false && !data.Asset) { data.Asset = ScriptableObject.CreateInstance <MapAsset>(); data.Asset.Settings = new Quantum.Map(); } BakeData(data, inEditor); }
public static void Init(Boolean force = false) { #if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN Quantum.Native.Impl = Quantum.Native.NativeImpl.MSCVCRT; #elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX || UNITY_IOS Quantum.Native.Impl = Quantum.Native.NativeImpl.LIBC; #elif UNITY_ANDROID Quantum.Native.Impl = Quantum.Native.NativeImpl.C; #elif UNITY_SWITCH && !UNITY_EDITOR unsafe { Quantum.Native.Impl = Quantum.Native.NativeImpl.DELEGATE; Quantum.Native.MemCpyDelegate = __Internal.MemCpyCaller; } #endif // load lookup table FPMathUtils.LoadLookupTables(force); // init profiler Quantum.Profiler.Init(x => UnityEngine.Profiling.Profiler.BeginSample(x), () => UnityEngine.Profiling.Profiler.EndSample()); // init debug draw functions Quantum.Draw.Init( Quantum.Core.DebugDraw.Ray, Quantum.Core.DebugDraw.Line, Quantum.Core.DebugDraw.Circle, Quantum.Core.DebugDraw.Rectangle ); // init quantum logger Quantum.Log.Init( UnityEngine.Debug.Log, UnityEngine.Debug.LogWarning, UnityEngine.Debug.LogError, UnityEngine.Debug.LogException ); // init photon logger Photon.Deterministic.DeterministicLog.Init( UnityEngine.Debug.Log, UnityEngine.Debug.LogWarning, UnityEngine.Debug.LogError, UnityEngine.Debug.LogException ); }
public static void BakeNavMeshes(MapData data, Boolean inEditor) { FPMathUtils.LoadLookupTables(); data.Asset.NavMeshes = new TextAsset[0]; var navmeshes = BakeNavMeshesLoop(data).ToArray(); if (inEditor) { #if UNITY_EDITOR var pathOnDisk = PathUtils.Combine('/', Path.GetDirectoryName(Application.dataPath), Path.GetDirectoryName(AssetDatabase.GetAssetPath(data.Asset))); var assetDir = Path.GetDirectoryName(AssetDatabase.GetAssetPath(data.Asset)); var assetPath = PathUtils.Combine('/', assetDir, Path.GetFileNameWithoutExtension(AssetDatabase.GetAssetPath(data.Asset))); foreach (var navmesh in navmeshes) { var navmeshFileOnDisk = data.Asset.name + "_" + navmesh.Name + ".bytes"; var navmeshAssetPath = assetPath + "_" + navmesh.Name + ".bytes"; // serialize (max 20 megabytes for now) var bytestream = new ByteStream(new Byte[1024 * 1024 * 20]); navmesh.Serialize(bytestream, true); // write File.WriteAllBytes(PathUtils.Combine('/', pathOnDisk, navmeshFileOnDisk), bytestream.ToArray()); // import asset AssetDatabase.ImportAsset(navmeshAssetPath, ImportAssetOptions.ForceUpdate); // add assets to navmehs ArrayUtils.Add(ref data.Asset.NavMeshes, AssetDatabase.LoadAssetAtPath <TextAsset>(navmeshAssetPath)); } #endif } else { data.Asset.InitNavMeshes(navmeshes); } }
void DrawGizmo(Boolean selected) { #if UNITY_EDITOR if (Vertices.Length >= 3) { FPMathUtils.LoadLookupTables(); var color = FPVector2.IsPolygonConvex(Vertices) ? ColorRGBA.ColliderGreen : ColorRGBA.Red; var normals = FPVector2.CalculatePolygonNormals(Vertices); UnityEditor.Handles.matrix = transform.localToWorldMatrix; UnityEditor.Handles.color = color.ToColor().Alpha(0.125f); UnityEditor.Handles.DrawAAConvexPolygon(Vertices.Select(x => x.ToUnityVector3()).ToArray()); var cw = FPVector2.IsClockWise(Vertices); Gizmos.color = color.ToColor().Alpha(selected ? 1f : 0.55f); Gizmos.matrix = transform.localToWorldMatrix; for (Int32 i = 0; i < Vertices.Length; ++i) { var v1 = Vertices[i].ToUnityVector3(); var v2 = Vertices[(i + 1) % Vertices.Length].ToUnityVector3(); var n = (cw ? -normals[i] : normals[i]).ToUnityVector3(); var c = Vector3.Lerp(v1, v2, 0.5f); Gizmos.DrawLine(v1, v2); Gizmos.DrawLine(c, c + (n * 0.25f)); } Gizmos.DrawWireSphere(FPVector2.CalculatePolygonCentroid(Vertices).ToUnityVector3(), 0.025f); } //Gizmos.DrawWireSphere(Vector3.zero, FPVector2.CalculatePolygonRadius(Vertices).AsFloat); Gizmos.color = UnityEditor.Handles.color = Color.white; Gizmos.matrix = UnityEditor.Handles.matrix = Matrix4x4.identity; #endif }
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 } }