/// <summary> /// Calculate the shortest path through the funnel. /// /// If the unwrap option is disabled the funnel will simply be projected onto the XZ plane. /// If the unwrap option is enabled then the funnel may be oriented arbitrarily and may have twists and bends. /// This makes it possible to support the funnel algorithm in XY space as well as in more complicated cases, such /// as on curved worlds. /// [Open online documentation to see images] /// /// [Open online documentation to see images] /// /// See: Unwrap /// </summary> /// <param name="funnel">The portals of the funnel. The first and last vertices portals must be single points (so for example left[0] == right[0]).</param> /// <param name="unwrap">Determines if twists and bends should be straightened out before running the funnel algorithm.</param> /// <param name="splitAtEveryPortal">If true, then a vertex will be inserted every time the path crosses a portal /// instead of only at the corners of the path. The result will have exactly one vertex per portal if this is enabled. /// This may introduce vertices with the same position in the output (esp. in corners where many portals meet).</param> public static List <Vector3> Calculate(FunnelPortals funnel, bool unwrap, bool splitAtEveryPortal) { if (funnel.left.Count != funnel.right.Count) { throw new System.ArgumentException("funnel.left.Count != funnel.right.Count"); } // Get arrays at least as large as the number of portals var leftArr = ArrayPool <Vector2> .Claim(funnel.left.Count); var rightArr = ArrayPool <Vector2> .Claim(funnel.left.Count); if (unwrap) { Unwrap(funnel, leftArr, rightArr); } else { // Copy to arrays for (int i = 0; i < funnel.left.Count; i++) { leftArr[i] = ToXZ(funnel.left[i]); rightArr[i] = ToXZ(funnel.right[i]); } } int startIndex = FixFunnel(leftArr, rightArr, funnel.left.Count); var intermediateResult = ListPool <int> .Claim(); if (startIndex == -1) { // If funnel algorithm failed, fall back to a simple line intermediateResult.Add(0); intermediateResult.Add(funnel.left.Count - 1); } else { bool lastCorner; Calculate(leftArr, rightArr, funnel.left.Count, startIndex, intermediateResult, int.MaxValue, out lastCorner); } // Get list for the final result var result = ListPool <Vector3> .Claim(intermediateResult.Count); Vector2 prev2D = leftArr[0]; var prevIdx = 0; for (int i = 0; i < intermediateResult.Count; i++) { var idx = intermediateResult[i]; if (splitAtEveryPortal) { // Check intersections with every portal segment var next2D = idx >= 0 ? leftArr[idx] : rightArr[-idx]; for (int j = prevIdx + 1; j < System.Math.Abs(idx); j++) { var factor = VectorMath.LineIntersectionFactorXZ(FromXZ(leftArr[j]), FromXZ(rightArr[j]), FromXZ(prev2D), FromXZ(next2D)); result.Add(Vector3.Lerp(funnel.left[j], funnel.right[j], factor)); } prevIdx = Mathf.Abs(idx); prev2D = next2D; } if (idx >= 0) { result.Add(funnel.left[idx]); } else { result.Add(funnel.right[-idx]); } } // Release lists back to the pool ListPool <int> .Release(ref intermediateResult); ArrayPool <Vector2> .Release(ref leftArr); ArrayPool <Vector2> .Release(ref rightArr); return(result); }
/** Builds a polygon mesh from a contour set. * * \param cset contour set to build a mesh from. * \param nvp Maximum allowed vertices per polygon. \warning Currently locked to 3. * \param mesh Results will be written to this mesh. */ public void BuildPolyMesh(VoxelContourSet cset, int nvp, out VoxelMesh mesh) { AstarProfiler.StartProfile("Build Poly Mesh"); nvp = 3; int maxVertices = 0; int maxTris = 0; int maxVertsPerCont = 0; for (int i = 0; i < cset.conts.Count; i++) { // Skip null contours. if (cset.conts[i].nverts < 3) { continue; } maxVertices += cset.conts[i].nverts; maxTris += cset.conts[i].nverts - 2; maxVertsPerCont = System.Math.Max(maxVertsPerCont, cset.conts[i].nverts); } Int3[] verts = ArrayPool <Int3> .Claim(maxVertices); int[] polys = ArrayPool <int> .Claim(maxTris *nvp); int[] areas = ArrayPool <int> .Claim(maxTris); Memory.MemSet <int>(polys, 0xff, sizeof(int)); int[] indices = ArrayPool <int> .Claim(maxVertsPerCont); int[] tris = ArrayPool <int> .Claim(maxVertsPerCont *3); int vertexIndex = 0; int polyIndex = 0; int areaIndex = 0; for (int i = 0; i < cset.conts.Count; i++) { VoxelContour cont = cset.conts[i]; // Skip degenerate contours if (cont.nverts < 3) { continue; } for (int j = 0; j < cont.nverts; j++) { indices[j] = j; // Convert the z coordinate from the form z*voxelArea.width which is used in other places for performance cont.verts[j * 4 + 2] /= voxelArea.width; } // Triangulate the contour int ntris = Triangulate(cont.nverts, cont.verts, ref indices, ref tris); // Assign the correct vertex indices int startIndex = vertexIndex; for (int j = 0; j < ntris * 3; polyIndex++, j++) { //@Error sometimes polys[polyIndex] = tris[j] + startIndex; } // Mark all triangles generated by this contour // as having the area cont.area for (int j = 0; j < ntris; areaIndex++, j++) { areas[areaIndex] = cont.area; } // Copy the vertex positions for (int j = 0; j < cont.nverts; vertexIndex++, j++) { verts[vertexIndex] = new Int3(cont.verts[j * 4], cont.verts[j * 4 + 1], cont.verts[j * 4 + 2]); } } mesh = new VoxelMesh { verts = Memory.ShrinkArray(verts, vertexIndex), tris = Memory.ShrinkArray(polys, polyIndex), areas = Memory.ShrinkArray(areas, areaIndex) }; ArrayPool <Int3> .Release(ref verts); ArrayPool <int> .Release(ref polys); ArrayPool <int> .Release(ref areas); ArrayPool <int> .Release(ref indices); ArrayPool <int> .Release(ref tris); AstarProfiler.EndProfile("Build Poly Mesh"); }
void SerializeUnityNavMesh(NavMeshTriangulation unityNavMesh, ref RecastGraph recast) { if (active == null || active.data == null) { return; } var vertMap = ObjectPoolSimple <Dictionary <int, int> > .Claim(); var totalVoxelWidth = (int)(recast.forcedBoundsSize.x / recast.cellSize + 0.5f); var totalVoxelDepth = (int)(recast.forcedBoundsSize.z / recast.cellSize + 0.5f); var tileSizeX = recast.editorTileSize; var tileSizeZ = recast.editorTileSize; var tileXCount = (totalVoxelWidth + tileSizeX - 1) / tileSizeX; var tileZCount = (totalVoxelDepth + tileSizeZ - 1) / tileSizeZ; var tileWorldSize = recast.TileWorldSizeX; var bucket = ArrayPool <List <int> > .Claim((tileXCount + 1) *(tileZCount + 1)); for (int i = 0; i < unityNavMesh.vertices.Length; i++) { var v = unityNavMesh.vertices[i]; var tileIndex = vertexOnTile( v, recast.forcedBoundsCenter, recast.forcedBoundsSize, tileWorldSize, tileXCount, tileZCount); tileIndex = 0; if (bucket[tileIndex] == null) { bucket[tileIndex] = ListPool <int> .Claim(); } bucket[tileIndex].Add(i); } foreach (var b in bucket) { if (b == null) { continue; } for (int i = 0; i < b.Count; i++) { for (int j = 0; j < i; j++) { if (b[i] >= unityNavMesh.vertices.Length || b[j] >= unityNavMesh.vertices.Length) { continue; } if (Vector3.Distance(unityNavMesh.vertices[b[i]], unityNavMesh.vertices[b[j]]) < 1e-3) { vertMap[b[i]] = b[j]; break; } } } } ArrayPool <List <int> > .Release(ref bucket, true); // only one tile recast.transform = recast.CalculateTransform(); recast.tileXCount = 1; recast.tileZCount = 1; recast.tileSizeX = totalVoxelWidth + 1; recast.tileSizeZ = totalVoxelDepth + 1; recast.ResetTiles(recast.tileXCount * recast.tileZCount); TriangleMeshNode.SetNavmeshHolder((int)recast.graphIndex, recast); var graphUpdateLock = active.PausePathfinding(); for (int z = 0; z < recast.tileZCount; z++) { for (int x = 0; x < recast.tileXCount; x++) { var tileOffset = recast.forcedBoundsCenter - recast.forcedBoundsSize * 0.5f + new Vector3( x * tileWorldSize, 0, z * tileWorldSize ); var trisClaim = ArrayPool <int> .Claim(unityNavMesh.indices.Length); var tris = Memory.ShrinkArray(trisClaim, unityNavMesh.indices.Length); ArrayPool <int> .Release(ref trisClaim, true); for (int i = 0; i < tris.Length; i++) { var tri = unityNavMesh.indices[i]; if (vertMap.ContainsKey(tri)) { tri = vertMap[tri]; } tris[i] = tri; } var vertsClaim = ArrayPool <Int3> .Claim(unityNavMesh.vertices.Length); var verts = Memory.ShrinkArray(vertsClaim, unityNavMesh.vertices.Length); ArrayPool <Int3> .Release(ref vertsClaim, true); for (int i = 0; i < verts.Length; i++) { var vertInWorld = unityNavMesh.vertices[i]; var vertInTile = vertInWorld - tileOffset; verts[i] = new Int3(vertInTile); } recast.ReplaceTile(x, z, 1, 1, verts, tris); } } graphUpdateLock.Release(); ObjectPoolSimple <Dictionary <int, int> > .Release(ref vertMap); }
/** Generates a terrain chunk mesh */ RasterizationMesh GenerateHeightmapChunk(float[, ] heights, Vector3 sampleSize, Vector3 offset, int x0, int z0, int width, int depth, int stride) { // Downsample to a smaller mesh (full resolution will take a long time to rasterize) // Round up the width to the nearest multiple of terrainSampleSize and then add 1 // (off by one because there are vertices at the edge of the mesh) int resultWidth = CeilDivision(width, terrainSampleSize) + 1; int resultDepth = CeilDivision(depth, terrainSampleSize) + 1; var heightmapWidth = heights.GetLength(0); var heightmapDepth = heights.GetLength(1); // Create a mesh from the heightmap var numVerts = resultWidth * resultDepth; var terrainVertices = ArrayPool <Vector3> .Claim(numVerts); // Create lots of vertices for (int z = 0; z < resultDepth; z++) { for (int x = 0; x < resultWidth; x++) { int sampleX = Math.Min(x0 + x * stride, heightmapWidth - 1); int sampleZ = Math.Min(z0 + z * stride, heightmapDepth - 1); terrainVertices[z * resultWidth + x] = new Vector3(sampleZ * sampleSize.x, heights[sampleX, sampleZ] * sampleSize.y, sampleX * sampleSize.z) + offset; } } // Create the mesh by creating triangles in a grid like pattern int numTris = (resultWidth - 1) * (resultDepth - 1) * 2 * 3; var tris = ArrayPool <int> .Claim(numTris); int triangleIndex = 0; for (int z = 0; z < resultDepth - 1; z++) { for (int x = 0; x < resultWidth - 1; x++) { tris[triangleIndex] = z * resultWidth + x; tris[triangleIndex + 1] = z * resultWidth + x + 1; tris[triangleIndex + 2] = (z + 1) * resultWidth + x + 1; triangleIndex += 3; tris[triangleIndex] = z * resultWidth + x; tris[triangleIndex + 1] = (z + 1) * resultWidth + x + 1; tris[triangleIndex + 2] = (z + 1) * resultWidth + x; triangleIndex += 3; } } #if ASTARDEBUG var color = AstarMath.IntToColor(x0 + 7 * z0, 0.7f); for (int i = 0; i < numTris; i += 3) { Debug.DrawLine(terrainVertices[tris[i]], terrainVertices[tris[i + 1]], color, 40); Debug.DrawLine(terrainVertices[tris[i + 1]], terrainVertices[tris[i + 2]], color, 40); Debug.DrawLine(terrainVertices[tris[i + 2]], terrainVertices[tris[i]], color, 40); } #endif var mesh = new RasterizationMesh(terrainVertices, tris, new Bounds()); mesh.numVertices = numVerts; mesh.numTriangles = numTris; mesh.pool = true; // Could probably calculate these bounds in a faster way mesh.RecalculateBounds(); return(mesh); }
public void BuildContours(float maxError, int maxEdgeLength, VoxelContourSet cset, int buildFlags) { AstarProfiler.StartProfile("Build Contours"); AstarProfiler.StartProfile("- Init"); int w = voxelArea.width; int d = voxelArea.depth; int wd = w * d; //cset.bounds = voxelArea.bounds; int maxContours = Mathf.Max(8 /*Max Regions*/, 8); //cset.conts = new VoxelContour[maxContours]; List <VoxelContour> contours = new List <VoxelContour>(maxContours); AstarProfiler.EndProfile("- Init"); AstarProfiler.StartProfile("- Mark Boundaries"); //cset.nconts = 0; //NOTE: This array may contain any data, but since we explicitly set all data in it before we use it, it's OK. ushort[] flags = voxelArea.tmpUShortArr; if (flags.Length < voxelArea.compactSpanCount) { flags = voxelArea.tmpUShortArr = new ushort[voxelArea.compactSpanCount]; } // Mark boundaries. (@?) for (int z = 0; z < wd; z += voxelArea.width) { for (int x = 0; x < voxelArea.width; x++) { CompactVoxelCell c = voxelArea.compactCells[x + z]; for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++) { ushort res = 0; CompactVoxelSpan s = voxelArea.compactSpans[i]; if (s.reg == 0 || (s.reg & BorderReg) == BorderReg) { flags[i] = 0; continue; } for (int dir = 0; dir < 4; dir++) { int r = 0; if (s.GetConnection(dir) != NotConnected) { int nx = x + voxelArea.DirectionX[dir]; int nz = z + voxelArea.DirectionZ[dir]; int ni = (int)voxelArea.compactCells[nx + nz].index + s.GetConnection(dir); r = voxelArea.compactSpans[ni].reg; } //@TODO - Why isn't this inside the previous IF if (r == s.reg) { res |= (ushort)(1 << dir); } } //Inverse, mark non connected edges. flags[i] = (ushort)(res ^ 0xf); } } } AstarProfiler.EndProfile("- Mark Boundaries"); AstarProfiler.StartProfile("- Simplify Contours"); List <int> verts = ListPool <int> .Claim(256); //new List<int> (256); List <int> simplified = ListPool <int> .Claim(64); //new List<int> (64); for (int z = 0; z < wd; z += voxelArea.width) { for (int x = 0; x < voxelArea.width; x++) { CompactVoxelCell c = voxelArea.compactCells[x + z]; for (int i = (int)c.index, ci = (int)(c.index + c.count); i < ci; i++) { //CompactVoxelSpan s = voxelArea.compactSpans[i]; if (flags[i] == 0 || flags[i] == 0xf) { flags[i] = 0; continue; } int reg = voxelArea.compactSpans[i].reg; if (reg == 0 || (reg & BorderReg) == BorderReg) { continue; } int area = voxelArea.areaTypes[i]; verts.Clear(); simplified.Clear(); WalkContour(x, z, i, flags, verts); SimplifyContour(verts, simplified, maxError, maxEdgeLength, buildFlags); RemoveDegenerateSegments(simplified); VoxelContour contour = new VoxelContour(); contour.verts = ArrayPool <int> .Claim(simplified.Count); //simplified.ToArray (); for (int j = 0; j < simplified.Count; j++) { contour.verts[j] = simplified[j]; } #if ASTAR_RECAST_INCLUDE_RAW_VERTEX_CONTOUR //Not used at the moment, just debug stuff contour.rverts = ClaimIntArr(verts.Count); for (int j = 0; j < verts.Count; j++) { contour.rverts[j] = verts[j]; } #endif contour.nverts = simplified.Count / 4; contour.reg = reg; contour.area = area; contours.Add(contour); #if ASTARDEBUG for (int q = 0, j = (simplified.Count / 4) - 1; q < (simplified.Count / 4); j = q, q++) { int i4 = q * 4; int j4 = j * 4; Vector3 p1 = Vector3.Scale( new Vector3( simplified[i4 + 0], simplified[i4 + 1], (simplified[i4 + 2] / (float)voxelArea.width) ), cellScale) + voxelOffset; Vector3 p2 = Vector3.Scale( new Vector3( simplified[j4 + 0], simplified[j4 + 1], (simplified[j4 + 2] / (float)voxelArea.width) ) , cellScale) + voxelOffset; if (CalcAreaOfPolygon2D(contour.verts, contour.nverts) > 0) { Debug.DrawLine(p1, p2, AstarMath.IntToColor(reg, 0.5F)); } else { Debug.DrawLine(p1, p2, Color.red); } } #endif } } } ListPool <int> .Release(ref verts); ListPool <int> .Release(ref simplified); AstarProfiler.EndProfile("- Simplify Contours"); AstarProfiler.StartProfile("- Fix Contours"); // Check and merge droppings. // Sometimes the previous algorithms can fail and create several contours // per area. This pass will try to merge the holes into the main region. for (int i = 0; i < contours.Count; i++) { VoxelContour cont = contours[i]; // Check if the contour is would backwards. if (CalcAreaOfPolygon2D(cont.verts, cont.nverts) < 0) { // Find another contour which has the same region ID. int mergeIdx = -1; for (int j = 0; j < contours.Count; j++) { if (i == j) { continue; } if (contours[j].nverts > 0 && contours[j].reg == cont.reg) { // Make sure the polygon is correctly oriented. if (CalcAreaOfPolygon2D(contours[j].verts, contours[j].nverts) > 0) { mergeIdx = j; break; } } } if (mergeIdx == -1) { Debug.LogError("rcBuildContours: Could not find merge target for bad contour " + i + "."); } else { // Debugging //Debug.LogWarning ("Fixing contour"); VoxelContour mcont = contours[mergeIdx]; // Merge by closest points. int ia = 0, ib = 0; GetClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ref ia, ref ib); if (ia == -1 || ib == -1) { Debug.LogWarning("rcBuildContours: Failed to find merge points for " + i + " and " + mergeIdx + "."); continue; } #if ASTARDEBUG int p4 = ia * 4; int p42 = ib * 4; Vector3 p12 = Vector3.Scale( new Vector3( mcont.verts[p4 + 0], mcont.verts[p4 + 1], (mcont.verts[p4 + 2] / (float)voxelArea.width) ), cellScale) + voxelOffset; Vector3 p22 = Vector3.Scale( new Vector3( cont.verts[p42 + 0], cont.verts[p42 + 1], (cont.verts[p42 + 2] / (float)voxelArea.width) ) , cellScale) + voxelOffset; Debug.DrawLine(p12, p22, Color.green); #endif if (!MergeContours(ref mcont, ref cont, ia, ib)) { Debug.LogWarning("rcBuildContours: Failed to merge contours " + i + " and " + mergeIdx + "."); continue; } contours[mergeIdx] = mcont; contours[i] = cont; #if ASTARDEBUG Debug.Log(mcont.nverts); for (int q = 0, j = (mcont.nverts) - 1; q < (mcont.nverts); j = q, q++) { int i4 = q * 4; int j4 = j * 4; Vector3 p1 = Vector3.Scale( new Vector3( mcont.verts[i4 + 0], mcont.verts[i4 + 1], (mcont.verts[i4 + 2] / (float)voxelArea.width) ), cellScale) + voxelOffset; Vector3 p2 = Vector3.Scale( new Vector3( mcont.verts[j4 + 0], mcont.verts[j4 + 1], (mcont.verts[j4 + 2] / (float)voxelArea.width) ) , cellScale) + voxelOffset; Debug.DrawLine(p1, p2, Color.red); //} } #endif } } } cset.conts = contours; AstarProfiler.EndProfile("- Fix Contours"); AstarProfiler.EndProfile("Build Contours"); }
// Token: 0x06002797 RID: 10135 RVA: 0x001B2B78 File Offset: 0x001B0D78 public static List <Vector3> Calculate(Funnel.FunnelPortals funnel, bool unwrap, bool splitAtEveryPortal) { if (funnel.left.Count != funnel.right.Count) { throw new ArgumentException("funnel.left.Count != funnel.right.Count"); } Vector2[] array = ArrayPool <Vector2> .Claim(funnel.left.Count); Vector2[] array2 = ArrayPool <Vector2> .Claim(funnel.left.Count); if (unwrap) { Funnel.Unwrap(funnel, array, array2); } else { for (int i = 0; i < funnel.left.Count; i++) { array[i] = Funnel.ToXZ(funnel.left[i]); array2[i] = Funnel.ToXZ(funnel.right[i]); } } int num = Funnel.FixFunnel(array, array2, funnel.left.Count); List <int> list = ListPool <int> .Claim(); if (num == -1) { list.Add(0); list.Add(funnel.left.Count - 1); } else { bool flag; Funnel.Calculate(array, array2, funnel.left.Count, num, list, int.MaxValue, out flag); } List <Vector3> list2 = ListPool <Vector3> .Claim(list.Count); Vector2 p = array[0]; int num2 = 0; for (int j = 0; j < list.Count; j++) { int num3 = list[j]; if (splitAtEveryPortal) { Vector2 vector = (num3 >= 0) ? array[num3] : array2[-num3]; for (int k = num2 + 1; k < Math.Abs(num3); k++) { float t = VectorMath.LineIntersectionFactorXZ(Funnel.FromXZ(array[k]), Funnel.FromXZ(array2[k]), Funnel.FromXZ(p), Funnel.FromXZ(vector)); list2.Add(Vector3.Lerp(funnel.left[k], funnel.right[k], t)); } num2 = Mathf.Abs(num3); p = vector; } if (num3 >= 0) { list2.Add(funnel.left[num3]); } else { list2.Add(funnel.right[-num3]); } } ListPool <int> .Release(ref list); ArrayPool <Vector2> .Release(ref array, false); ArrayPool <Vector2> .Release(ref array2, false); return(list2); }