예제 #1
0
        /// <summary>
        /// Root
        /// </summary>
        private SearchItem(int tri)
        {
            triangle = tri;
            directDistToDest = 0;
            previous = null;

            distFromStart = 0;
            nodeCount = 0;
        }
예제 #2
0
        public SearchItem(int triangle, int edge, float directDistToDest, ref Vector3 pointOfReference, SearchItem previous)
        {
            this.triangle = triangle;
            this.edge = edge;
            this.directDistToDest = directDistToDest;
            this.previous = previous;
            this.PointOfReference = pointOfReference;

            distFromStart = previous.distFromStart + Vector3.Distance(previous.PointOfReference, pointOfReference);
            nodeCount = previous.nodeCount + 1;
        }
예제 #3
0
 public NodeItem(Vector3 node, SearchItem item)
 {
     Node = node;
     Item = item;
 }
예제 #4
0
        public void FindPath(Vector3 destination, SearchItem corridor, Path path)
        {
            var mesh = Tile.NavMesh;

            path.Reset(corridor.NodeCount);
            path.Add(destination);             // the destination is the final vertex of the path

            if (corridor.NodeCount < 3)
            {
                if (corridor.NodeCount == 2)
                {
                    // Two neighboring nodes:

                    Vector3 newPoint;
                    Vector3 right, left;

                    // TODO: Project into 2d, so we can intersect two lines instead of a line and a plane

                    // greedily span a vertical plane over the line between start and destination
                    var start = corridor.Previous.PointOfReference;
                    //var plane = new Plane(start, destination, new Vector3(start.X, start.Y, start.Z + 10));

                    var startXY = corridor.Previous.PointOfReference.XY;
                    var endXY   = destination.XY;

                    // and then intersect the plane with the edge that we want to traverse
                    mesh.GetInsideOrderedEdgePoints(corridor.Previous.Triangle, corridor.Edge, out right, out left);
                    var rightXY = right.XY;
                    var leftXY  = left.XY;

                    //if (!Intersection.ClosestPointToPlane(right, left, plane, out newPoint))
                    //{
                    // this is geometrically impossible but might happen due to numerical inaccuracy
                    //Debugger.Break();
                    //	newPoint = right;
                    //}

                    Vector2 newPointXY;
                    if (Intersection.IntersectionOfLineSegments2D(startXY, endXY, rightXY, leftXY, out newPointXY))
                    {
                        float newZ;
                        if (newPointXY.LerpZ(start, destination, out newZ))
                        {
                            newPoint = new Vector3(newPointXY, newZ);
                        }
                        else
                        {
                            // this is geometrically impossible but might happen due to numerical inaccuracy
                            Debugger.Break();
                            newPoint = right;
                        }
                    }
                    else
                    {
                        // this is geometrically impossible but might happen due to numerical inaccuracy
                        Debugger.Break();
                        newPoint = right;
                    }

                    path.Add(newPoint);
                }
            }
            else
            {
                // More than two nodes:


                //// if distance to new point is smaller than the threshold, replace, rather than add
                //var distToLast = lastLeft
                //if (distToLast < MinWaypointThreshold)
                //{

                //}
                // steer backwards through the corridor
                //Vector3 leftOrigin = destination, rightOrigin = destination;
                var     origin = destination;
                Vector3 lastRight, lastLeft;
                int     lastLeftIdx = -1, lastRightIdx = -1;
                var     current = corridor;
                mesh.GetInsideOrderedEdgePoints(current.Previous.Triangle, current.Edge, out lastRight, out lastLeft);
                for (var i = corridor.NodeCount - 2; i > 0; i--)
                {
                    current = current.Previous;

                    // get the two points that span the traversed edge
                    int leftIndex, rightIndex;
                    mesh.GetEdgePointIndices(current.Previous.Triangle, current.Edge, out rightIndex, out leftIndex);

                    var addedLeft = false;
                    if (leftIndex != lastLeftIdx)
                    {
                        // cast new ray and see if the last point is still inside the corridor
                        var left       = mesh.Vertices[leftIndex];
                        var lastLeft2D = lastRight.XY;
                        var dir        = left.XY - origin.XY;
                        var normal     = dir.RightNormal();

                        var leftOrig2D = origin.XY;
                        var toLastLeft = lastLeft2D - leftOrig2D;

                        var dist = Vector2.Dot(toLastLeft, normal);

                        if (dist - MathUtil.Epsilonf > 0)
                        {
                            // right of the left ray -> put obstructing vertex into path
                            origin = lastLeft;
                            path.Add(lastLeft);
                            addedLeft = true;
                        }

                        lastLeft    = left;
                        lastLeftIdx = leftIndex;
                    }

                    if (rightIndex != lastRightIdx)
                    {
                        var right = mesh.Vertices[rightIndex];
                        if (!addedLeft)
                        {
                            // cast new ray and see if the last point is still inside the corridor
                            var lastRight2D = lastLeft.XY;
                            var dir         = right.XY - origin.XY;
                            var normal      = dir.RightNormal();

                            var rightOrig2D = origin.XY;
                            var toLastRight = lastRight2D - rightOrig2D;

                            var dist = Vector2.Dot(toLastRight, normal);

                            if (dist + MathUtil.Epsilonf < 0)
                            {
                                // left of the right ray -> put obstructing vertex into path
                                origin = lastRight;
                                path.Add(lastRight);
                            }
                        }
                        lastRight    = right;
                        lastRightIdx = rightIndex;
                    }
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Basic A* implementation that searches through the navmesh.
        /// </summary>
        public SearchItem FindCorridor(Vector3 start, Vector3 destination,
                                       out System.Collections.Generic.HashSet <int> visited)
        {
            var mesh = NavMesh;

            visited = null;
            if (mesh == null)
            {
                return(SearchItem.Null);
            }

            var triStart = mesh.FindFirstTriangleUnderneath(start);
            var triEnd   = mesh.FindFirstTriangleUnderneath(destination);

            if (triStart == -1 || triEnd == -1)
            {
                visited = null;
                return(SearchItem.Null);
            }

            visited = new System.Collections.Generic.HashSet <int> {
                triStart
            };

            // the start location is the first node
            var current = new SearchItem(triStart, -1, Vector3.Distance(start, destination), ref start);

            if (triStart == triEnd)
            {
                return(current);
            }

            // create fringe
            var fringe = new IntervalHeap <SearchItem>(comparer)
            {
                current
            };

            // start search
            var corridor = SearchItem.Null;

            do
            {
                // next round
                current = fringe.DeleteMin();

                // get the vertices and neighbors of the current triangle
                //var triangle = mesh.GetTriangle(current.Triangle);
                var neighbors = mesh.GetNeighborsOf(current.Triangle);

                //var poly = ((NavMesh)Mesh).Polygons[current.Triangle / 3];

                // iterate over all neighbors
                for (var edge = 0; edge < TerrainUtil.NeighborsPerTriangle; edge++)
                {
                    var neighbor = neighbors[edge] * 3;

                    if (neighbor < 0 || visited.Contains(neighbor))
                    {
                        continue;
                    }

                    //var npoly = ((NavMesh)Mesh).Polygons[neighbor / 3];
                    //var ntri = Mesh.GetTriangle(neighbor);

                    visited.Add(neighbor);


                    // determine the edge that we want to traverse

                    Vector3 left, right;
                    mesh.GetInsideOrderedEdgePoints(current.Triangle, edge, out right, out left);
                    //switch (edge)
                    //{
                    //    case TerrainUtil.ABEdgeIndex:
                    //        left = triangle.Point1;
                    //        right = triangle.Point2;
                    //        break;
                    //    case TerrainUtil.CAEdgeIndex:
                    //        left = triangle.Point3;
                    //        right = triangle.Point1;
                    //        break;
                    //    case TerrainUtil.BCEdgeIndex:
                    //        left = triangle.Point2;
                    //        right = triangle.Point3;
                    //        break;
                    //    default:
                    //        throw new Exception("Impossible");
                    //}

                    var refPoint = (left + right) / 2.0f;                               // the middle of the edge to be traversed
                    var dist     = Vector3.Distance(refPoint, destination);
                    var newItem  = new SearchItem(neighbor, edge, dist, ref refPoint, current);

                    if (triEnd == neighbor)
                    {
                        // we are done
                        corridor = newItem;
                        goto Done;
                    }

                    fringe.Add(newItem);
                }
            } while (!fringe.IsEmpty);

Done:
            return(corridor);
        }
예제 #6
0
        public void FindPathStringPull(Vector3 origin3d, Vector3 destination3d, SearchItem corridor, Path path)
        {
            var mesh          = NavMesh;
            var corridorStart = corridor;
            var pathNodes     = new List <NodeItem>(corridor.NodeCount);

            pathNodes.Add(new NodeItem(destination3d, corridorStart));

            if (corridor.Edge == -1) // origin and destination are within the same triangle
            {
                pathNodes.Add(new NodeItem(origin3d, corridorStart));
                path.Reset(pathNodes.Count);
                path.Add(pathNodes);
                return;
            }

            Vector3 curTriApex3d, curTriLeft3d, curTriRight3d;
            Vector3 prevTriApex3d, prevTriLeft3d, prevTriRight3d;
            Vector2 curTriApex, destination, curTriLeft, curTriRight;
            int     curTriangle, curEdge;

            bool apexInFunnelLeft;
            bool apexInFunnelRight;

            while (corridor.Previous.Previous != SearchItem.Null) // The next triangle contains the starting point
            {
                curTriangle = corridor.Previous.Triangle;
                curEdge     = corridor.Edge;
                mesh.GetOutsideOrderedEdgePointsPlusApex(curTriangle, curEdge, out curTriRight3d, out curTriLeft3d,
                                                         out curTriApex3d);

                destination = destination3d.XY;
                curTriApex  = curTriApex3d.XY;
                curTriLeft  = curTriLeft3d.XY;
                curTriRight = curTriRight3d.XY;

                var foundNewDestination = false;
                while (!foundNewDestination) // Found new point for the path
                {
                    if (corridor.Previous.Previous == SearchItem.Null)
                    {
                        break;
                    }

                    apexInFunnelLeft  = GeometryHelpers.IsRightOfOrOn(curTriApex, destination, curTriLeft);
                    apexInFunnelRight = GeometryHelpers.IsLeftOfOrOn(curTriApex, destination, curTriRight);

                    if (apexInFunnelLeft && apexInFunnelRight)
                    {
                        // the apex of the next triangle in the corridor is within the funnel. Narrow the funnel.
                        var prevEdge     = corridor.Previous.Edge;
                        var prevTriangle = corridor.Previous.Previous.Triangle;
                        mesh.GetOutsideOrderedEdgePointsPlusApex(prevTriangle, prevEdge, out prevTriRight3d,
                                                                 out prevTriLeft3d, out prevTriApex3d);

                        var prevTriApex  = prevTriApex3d.XY;
                        var prevTriLeft  = prevTriLeft3d.XY;
                        var prevTriRight = prevTriRight3d.XY;

                        if (curTriLeft == prevTriLeft)
                        {
                            curTriRight3d = curTriApex3d;
                            curTriRight   = curTriApex;
                        }
                        else if (curTriRight == prevTriRight)
                        {
                            curTriLeft3d = curTriApex3d;
                            curTriLeft   = curTriApex;
                        }
                        else
                        {
                            throw new Exception("WTF! You screwed the pooch here pal, find the error.");
                        }

                        curTriApex3d = prevTriApex3d;
                        curTriApex   = prevTriApex;
                    }
                    else if (!apexInFunnelLeft && apexInFunnelRight)
                    {
                        destination3d = curTriLeft3d;
                        pathNodes.Add(new NodeItem(destination3d, corridor.Previous));
                        //path.Add(destination3d);
                        foundNewDestination = true;
                    }
                    else if (apexInFunnelLeft)
                    {
                        destination3d = curTriRight3d;
                        pathNodes.Add(new NodeItem(destination3d, corridor.Previous));
                        //path.Add(destination3d);
                        foundNewDestination = true;
                    }
                    else
                    {
                        throw new Exception("This should never happen.");
                    }

                    corridor = corridor.Previous;
                }
            }

            // The next triangle in the corridor contains the starting point
            curTriangle = corridor.Previous.Triangle;
            curEdge     = corridor.Edge;
            mesh.GetOutsideOrderedEdgePointsPlusApex(curTriangle, curEdge, out curTriRight3d, out curTriLeft3d,
                                                     out curTriApex3d);
            destination       = destination3d.XY;
            curTriApex        = origin3d.XY;
            curTriLeft        = curTriLeft3d.XY;
            curTriRight       = curTriRight3d.XY;
            apexInFunnelLeft  = GeometryHelpers.IsRightOfOrOn(curTriApex, destination, curTriLeft);
            apexInFunnelRight = GeometryHelpers.IsLeftOfOrOn(curTriApex, destination, curTriRight);

            if (apexInFunnelLeft && apexInFunnelRight)
            {
                // the starting point of the corridor is within the funnel. We're done.
                pathNodes.Add(new NodeItem(origin3d, corridor.Previous));
                //path.Add(origin3d);
            }
            else if (!apexInFunnelLeft && apexInFunnelRight)
            {
                // the starting point of the corridor is outside the funnel on the left
                //add the left point and then the starting point
                pathNodes.Add(new NodeItem(curTriLeft3d, corridor.Previous));
                pathNodes.Add(new NodeItem(origin3d, corridor.Previous));
            }
            else if (apexInFunnelLeft)
            {
                // the starting point of the corridor is outside the funnel on the right
                //add the right point and then the starting point
                pathNodes.Add(new NodeItem(curTriRight3d, corridor.Previous));
                pathNodes.Add(new NodeItem(origin3d, corridor.Previous));
            }
            else
            {
                throw new Exception("This should never happen.");
            }

            StringPull(pathNodes);
            path.Reset(pathNodes.Count);
            path.Add(pathNodes);
        }
예제 #7
0
 public NodeItem(Vector3 node, SearchItem item)
 {
     Node = node;
     Item = item;
 }
예제 #8
0
        public SearchItem(int triangle, int edge, float directDistToDest, ref Vector3 pointOfReference, SearchItem previous)
        {
            this.triangle         = triangle;
            this.edge             = edge;
            this.directDistToDest = directDistToDest;
            this.previous         = previous;
            this.PointOfReference = pointOfReference;

            distFromStart = previous.distFromStart + Vector3.Distance(previous.PointOfReference, pointOfReference);
            nodeCount     = previous.nodeCount + 1;
        }