/// <summary>Copy the mesh to the vertex and triangle buffers after the vertices have been transformed using the inverse of the inverseTransform parameter.</summary> /// <param name="vbuffer">Assumed to be either null or an array which has a length of zero or a power of two. If this mesh has more /// vertices than can fit in the buffer then the buffer will be pooled using Pathfinding.Util.ArrayPool.Release and /// a new sufficiently large buffer will be taken from the pool.</param> /// <param name="tbuffer">This will be set to the internal triangle buffer. You must not modify this array.</param> /// <param name="inverseTransform">All vertices will be transformed using the #Pathfinding.GraphTransform.InverseTransform method. /// This is typically used to transform from world space to graph space.</param> public void GetMesh(ref Int3[] vbuffer, out int[] tbuffer, Pathfinding.Util.GraphTransform inverseTransform = null) { if (verts == null) { RebuildMesh(); } if (verts == null) { tbuffer = Util.ArrayPool <int> .Claim(0); return; } if (vbuffer == null || vbuffer.Length < verts.Length) { if (vbuffer != null) { Util.ArrayPool <Int3> .Release(ref vbuffer); } vbuffer = Util.ArrayPool <Int3> .Claim(verts.Length); } tbuffer = tris; if (useRotationAndScale) { Matrix4x4 m = Matrix4x4.TRS(tr.position + center, tr.rotation, tr.localScale * meshScale); for (int i = 0; i < verts.Length; i++) { var v = m.MultiplyPoint3x4(verts[i]); if (inverseTransform != null) { v = inverseTransform.InverseTransform(v); } vbuffer[i] = (Int3)v; } } else { Vector3 voffset = tr.position + center; for (int i = 0; i < verts.Length; i++) { var v = voffset + verts[i] * meshScale; if (inverseTransform != null) { v = inverseTransform.InverseTransform(v); } vbuffer[i] = (Int3)v; } } }
/** Bounds in XZ space after transforming using the *inverse* transform of the \a inverseTranform parameter. * The transformation will typically transform the vertices to graph space and this is used to * figure out which tiles the cut intersects. */ internal override Rect GetBounds(Pathfinding.Util.GraphTransform inverseTranform) { var buffers = Pathfinding.Util.ListPool <List <Vector3> > .Claim(); GetContour(buffers); Rect r = new Rect(); for (int i = 0; i < buffers.Count; i++) { var buffer = buffers[i]; for (int k = 0; k < buffer.Count; k++) { var p = inverseTranform.InverseTransform(buffer[k]); if (k == 0) { r = new Rect(p.x, p.z, 0, 0); } else { r.xMax = System.Math.Max(r.xMax, p.x); r.yMax = System.Math.Max(r.yMax, p.z); r.xMin = System.Math.Min(r.xMin, p.x); r.yMin = System.Math.Min(r.yMin, p.z); } } } Pathfinding.Util.ListPool <List <Vector3> > .Release(ref buffers); return(r); }
public void VoxelizeInput(Pathfinding.Util.GraphTransform graphTransform, Bounds graphSpaceBounds) { AstarProfiler.StartProfile("Build Navigation Mesh"); AstarProfiler.StartProfile("Voxelizing - Step 1"); // Transform from voxel space to graph space. // then scale from voxel space (one unit equals one voxel) // Finally add min Matrix4x4 voxelMatrix = Matrix4x4.TRS(graphSpaceBounds.min, Quaternion.identity, Vector3.one) * Matrix4x4.Scale(new Vector3(cellSize, cellHeight, cellSize)); transformVoxel2Graph = new Pathfinding.Util.GraphTransform(voxelMatrix); // Transform from voxel space to world space // add half a voxel to fix rounding transform = graphTransform * voxelMatrix * Matrix4x4.TRS(new Vector3(0.5f, 0, 0.5f), Quaternion.identity, Vector3.one); int maximumVoxelYCoord = (int)(graphSpaceBounds.size.y / cellHeight); AstarProfiler.EndProfile("Voxelizing - Step 1"); AstarProfiler.StartProfile("Voxelizing - Step 2 - Init"); // Cosine of the slope limit in voxel space (some tweaks are needed because the voxel space might be stretched out along the y axis) float slopeLimit = Mathf.Cos(Mathf.Atan(Mathf.Tan(maxSlope * Mathf.Deg2Rad) * (cellSize / cellHeight))); // Temporary arrays used for rasterization var clipperOrig = new VoxelPolygonClipper(3); var clipperX1 = new VoxelPolygonClipper(7); var clipperX2 = new VoxelPolygonClipper(7); var clipperZ1 = new VoxelPolygonClipper(7); var clipperZ2 = new VoxelPolygonClipper(7); if (inputMeshes == null) { throw new System.NullReferenceException("inputMeshes not set"); } // Find the largest lengths of vertex arrays and check for meshes which can be skipped int maxVerts = 0; for (int m = 0; m < inputMeshes.Count; m++) { maxVerts = System.Math.Max(inputMeshes[m].vertices.Length, maxVerts); } // Create buffer, here vertices will be stored multiplied with the local-to-voxel-space matrix var verts = new Vector3[maxVerts]; AstarProfiler.EndProfile("Voxelizing - Step 2 - Init"); AstarProfiler.StartProfile("Voxelizing - Step 2"); // This loop is the hottest place in the whole rasterization process // it usually accounts for around 50% of the time for (int m = 0; m < inputMeshes.Count; m++) { RasterizationMesh mesh = inputMeshes[m]; var meshMatrix = mesh.matrix; // Flip the orientation of all faces if the mesh is scaled in such a way // that the face orientations would change // This happens for example if a mesh has a negative scale along an odd number of axes // e.g it happens for the scale (-1, 1, 1) but not for (-1, -1, 1) or (1,1,1) var flipOrientation = VectorMath.ReversesFaceOrientations(meshMatrix); Vector3[] vs = mesh.vertices; int[] tris = mesh.triangles; int trisLength = mesh.numTriangles; // Transform vertices first to world space and then to voxel space for (int i = 0; i < vs.Length; i++) { verts[i] = transform.InverseTransform(meshMatrix.MultiplyPoint3x4(vs[i])); } int mesharea = mesh.area; for (int i = 0; i < trisLength; i += 3) { Vector3 p1 = verts[tris[i]]; Vector3 p2 = verts[tris[i + 1]]; Vector3 p3 = verts[tris[i + 2]]; if (flipOrientation) { var tmp = p1; p1 = p3; p3 = tmp; } int minX = (int)(Utility.Min(p1.x, p2.x, p3.x)); int minZ = (int)(Utility.Min(p1.z, p2.z, p3.z)); int maxX = (int)System.Math.Ceiling(Utility.Max(p1.x, p2.x, p3.x)); int maxZ = (int)System.Math.Ceiling(Utility.Max(p1.z, p2.z, p3.z)); minX = Mathf.Clamp(minX, 0, voxelArea.width - 1); maxX = Mathf.Clamp(maxX, 0, voxelArea.width - 1); minZ = Mathf.Clamp(minZ, 0, voxelArea.depth - 1); maxZ = Mathf.Clamp(maxZ, 0, voxelArea.depth - 1); // Check if the mesh is completely out of bounds if (minX >= voxelArea.width || minZ >= voxelArea.depth || maxX <= 0 || maxZ <= 0) { continue; } Vector3 normal; int area; //AstarProfiler.StartProfile ("Rasterize..."); normal = Vector3.Cross(p2 - p1, p3 - p1); float cosSlopeAngle = Vector3.Dot(normal.normalized, Vector3.up); if (cosSlopeAngle < slopeLimit) { area = UnwalkableArea; } else { area = 1 + mesharea; } clipperOrig[0] = p1; clipperOrig[1] = p2; clipperOrig[2] = p3; clipperOrig.n = 3; for (int x = minX; x <= maxX; x++) { clipperOrig.ClipPolygonAlongX(ref clipperX1, 1f, -x + 0.5f); if (clipperX1.n < 3) { continue; } clipperX1.ClipPolygonAlongX(ref clipperX2, -1F, x + 0.5F); if (clipperX2.n < 3) { continue; } float clampZ1 = clipperX2.z[0]; float clampZ2 = clipperX2.z[0]; for (int q = 1; q < clipperX2.n; q++) { float val = clipperX2.z[q]; clampZ1 = System.Math.Min(clampZ1, val); clampZ2 = System.Math.Max(clampZ2, val); } int clampZ1I = Mathf.Clamp((int)System.Math.Round(clampZ1), 0, voxelArea.depth - 1); int clampZ2I = Mathf.Clamp((int)System.Math.Round(clampZ2), 0, voxelArea.depth - 1); for (int z = clampZ1I; z <= clampZ2I; z++) { clipperX2.ClipPolygonAlongZWithYZ(ref clipperZ1, 1F, -z + 0.5F); if (clipperZ1.n < 3) { continue; } clipperZ1.ClipPolygonAlongZWithY(ref clipperZ2, -1F, z + 0.5F); if (clipperZ2.n < 3) { continue; } float sMin = clipperZ2.y[0]; float sMax = clipperZ2.y[0]; for (int q = 1; q < clipperZ2.n; q++) { float val = clipperZ2.y[q]; sMin = System.Math.Min(sMin, val); sMax = System.Math.Max(sMax, val); } int maxi = (int)System.Math.Ceiling(sMax); // Skip span if below or above the bounding box if (maxi >= 0 && sMin <= maximumVoxelYCoord) { // Make sure mini >= 0 int mini = System.Math.Max(0, (int)sMin); // Make sure the span is at least 1 voxel high maxi = System.Math.Max(mini + 1, maxi); voxelArea.AddLinkedSpan(z * voxelArea.width + x, (uint)mini, (uint)maxi, area, voxelWalkableClimb); } } } } //AstarProfiler.EndFastProfile(0); //AstarProfiler.EndProfile ("Rasterize..."); } AstarProfiler.EndProfile("Voxelizing - Step 2"); }
/** Y coordinate of the center of the bounding box in graph space */ internal float GetY(Pathfinding.Util.GraphTransform transform) { return(transform.InverseTransform(useRotationAndScale ? tr.TransformPoint(center) : tr.position + center).y); }
/** Y coordinate of the center of the bounding box in graph space */ internal float GetY(Pathfinding.Util.GraphTransform transform) { return(transform.InverseTransform(cutPos + center).y); }