public float SphereCost(CameraGraphPortalNode Start, CameraGraphPortalNode End, CameraGraphSphereNode Focus) { // From http://www.oskam.ch/research/camera_control_2009.pdf Section 4.1 // C(e_ij) = d(i,j) + a*d(i,j)(1-v(e_ij) // How much the cost is influenced by visibility. const float Alpha = 100f; // First, find the sphere shared between the portals CameraGraphSphereNode Sphere; if (Start.A == End.A) { Sphere = Start.A; } else { Sphere = Start.B; } float Dist = (Start.transform.position - End.transform.position).magnitude; float Visibility = 0; if (!Sphere.VisibilityValues.TryGetValue(Focus, out Visibility)) { Debug.Log("Couldn't get visibility from " + Sphere + " to " + Focus); Debug.DrawLine(Sphere.transform.position, Focus.transform.position, Color.magenta, 1.0f); } return(Dist + Alpha * Dist * (1 - Visibility)); }
IEnumerator GeneratePortals() { Debug.Log("Generating Portals"); // Tell each sphere node to connect to it's neighbors // I'm gonna use n^2 because spheres are fast for (int i = 0; i < Spheres.Count; ++i) { CameraGraphSphereNode A = Spheres[i] as CameraGraphSphereNode; for (int j = i + 1; j < Spheres.Count; ++j) { CameraGraphSphereNode B = Spheres[j] as CameraGraphSphereNode; // Check if the nodes intersect, and create a portal if they do if (A.Intersects(B)) { CameraGraphPortalNode Node = ConnectPortal(A, B); Portals.Add(Node); // Yield to main thread if we've done enough operations if (ShouldOperationYield()) { yield return(new WaitForEndOfFrame()); } } } } yield return(null); }
CameraPath ComputePath(Vector3 Start, Vector3 End, Vector3 Focus) { CameraPath Path = new CameraPath(); Path.Next = Start; // Figure out what portals we want CameraGraphPortalNode StartNode = Graph.GetClosestPortal(Start); CameraGraphPortalNode EndNode = Graph.GetClosestPortal(End); CameraGraphSphereNode FocusEdge = Graph.GetClosestSphere(Focus); // A* that shit! List <CameraGraphPortalNode> PortalPath = GetPortalPath(StartNode, EndNode, FocusEdge); // Get the points as positions foreach (CameraGraphPortalNode Portal in PortalPath) { Path.Points.Enqueue(Portal.transform.position); } // Finally, add the endpoint to the back of the queue Path.Points.Enqueue(End); return(Path); }
public float CostEstimate(CameraGraphPortalNode Start, CameraGraphPortalNode End) { // From http://www.oskam.ch/research/camera_control_2009.pdf Section 4.1 // H(n) = d(n, e) float Dist = (End.transform.position - Start.transform.position).magnitude; return(Dist); }
private IEnumerator LinkPortals() { Debug.Log("Linking Portals"); // Figure out how big our graph is int NumNodes = Portals.Count; int NumArcs = 0; // The number of arcs is equal to the sum of the number of spheres exit arcs - 1 for (int i = 0; i < SphereGraph.numNodes; ++i) { NumArcs += SphereGraph.NumArcsForNode(i) - 1; } // Remember to double your stuff int BidirectionOffset = NumArcs; NumArcs *= 2; PortalGraph = new SimpleGraph(NumNodes, NumArcs); // We can just look at all the spheres and connect their portals to make sure the web is complete foreach (CameraGraphSphereNode Sphere in Spheres) { int SphereIndex = Spheres.IndexOf(Sphere); for (int i = 0; i < Sphere.IntersectionPortals.Count; ++i) { CameraGraphPortalNode A = Sphere.IntersectionPortals[i] as CameraGraphPortalNode; for (int j = i; j < Sphere.IntersectionPortals.Count; ++j) { CameraGraphPortalNode B = Sphere.IntersectionPortals[j] as CameraGraphPortalNode; // Indices for the sphere nodes int PortalAIndex = Portals.IndexOf(A); int PortalBIndex = Portals.IndexOf(B); // Link A and B bidirectionally PortalGraph.SetNodeArc(PortalAIndex, SphereIndex, PortalBIndex); PortalGraph.SetNodeArc(PortalBIndex, SphereIndex + BidirectionOffset, PortalAIndex); // Debug draw Debug.DrawLine(A.transform.position, B.transform.position, Color.magenta, 1.0f); // Yield to draw if (ShouldOperationYield()) { yield return(new WaitForEndOfFrame()); } } } } Debug.Log("Linked " + PortalGraph.numNodes + " spheres with " + PortalGraph.numArcs + " arcs"); yield return(null); }
public CameraGraphPortalNode GetClosestPortal(Vector3 Position) { float BestSqrDist = Mathf.Infinity; CameraGraphPortalNode Best = Portals[0] as CameraGraphPortalNode; // This is unoptimized foreach (CameraGraphPortalNode Portal in Portals) { float SqrDist = (Position - Portal.transform.position).sqrMagnitude; if (SqrDist < BestSqrDist) { BestSqrDist = SqrDist; Best = Portal; } } return(Best); }
CameraGraphPortalNode GetLowest(Dictionary <CameraGraphPortalNode, float> D, HashSet <CameraGraphPortalNode> Set) { CameraGraphPortalNode BestPortal = null; float BestValue = Mathf.Infinity; foreach (CameraGraphPortalNode Portal in Set) { float PortalValue = Mathf.Infinity; D.TryGetValue(Portal, out PortalValue); if (PortalValue < BestValue) { BestValue = PortalValue; BestPortal = Portal; } } return(BestPortal); }
private CameraGraphPortalNode ConnectPortal(CameraGraphSphereNode A, CameraGraphSphereNode B) { // Create the child object GameObject PortalObj = new GameObject("Portal"); PortalObj.transform.SetParent(transform); PortalObj.layer = gameObject.layer; // Attatch the portal node logic CameraGraphPortalNode Portal = PortalObj.AddComponent <CameraGraphPortalNode>(); Portal.Connect(A, B); Portal.Graph = this; // Tell the Spheres that there's a portal connecting them A.IntersectionPortals.Add(Portal); B.IntersectionPortals.Add(Portal); return(Portal); }
List <CameraGraphPortalNode> ReconstructPath(Dictionary <CameraGraphPortalNode, CameraGraphPortalNode> CameFrom, CameraGraphPortalNode Current) { List <CameraGraphPortalNode> Total = new List <CameraGraphPortalNode>(); Total.Add(Current); while (CameFrom.ContainsKey(Current)) { Current = CameFrom[Current]; Total.Insert(0, Current); } return(Total); }
private List <CameraGraphPortalNode> GetPortalPath(CameraGraphPortalNode Start, CameraGraphPortalNode End, CameraGraphSphereNode Target) { // Nodes already evaluated HashSet <CameraGraphPortalNode> Closed = new HashSet <CameraGraphPortalNode>(); // Nodes to be evaluated HashSet <CameraGraphPortalNode> Open = new HashSet <CameraGraphPortalNode>(); // How we got to each node we visit Dictionary <CameraGraphPortalNode, CameraGraphPortalNode> CameFrom = new Dictionary <CameraGraphPortalNode, CameraGraphPortalNode>(); // The value of each of the nodes we've visited Dictionary <CameraGraphPortalNode, float> GScore = new Dictionary <CameraGraphPortalNode, float>(); // The estimated total cost from start to each node Dictionary <CameraGraphPortalNode, float> FScore = new Dictionary <CameraGraphPortalNode, float>(); // We start at the begining Open.Add(Start); GScore[Start] = 0; FScore[Start] = Graph.CostEstimate(Start, End); // Now the looping while (Open.Count != 0) { // Current portal is the node in open with the lowest F score value CameraGraphPortalNode Current = GetLowest(FScore, Open); // Are we there? if (Current == End) { return(ReconstructPath(CameFrom, Current)); } // Mark the node as visited Open.Remove(Current); Closed.Add(Current); Debug.Log("Visited " + Current); // Add the node's neighbors to the open set ArrayList Neighbors = new ArrayList(); Neighbors.AddRange(Current.A.IntersectionPortals); Neighbors.AddRange(Current.B.IntersectionPortals); foreach (CameraGraphPortalNode Neighbor in Neighbors) { // Skip nodes we've already visited if (Closed.Contains(Neighbor)) { continue; } float TentativeGScore = GScore[Current] + Graph.SphereCost(Current, Neighbor, Target); // If the node is good enough, then add it to our path if (!Open.Contains(Neighbor) || TentativeGScore < GScore[Neighbor]) { CameFrom[Neighbor] = Current; GScore[Neighbor] = TentativeGScore; FScore[Neighbor] = TentativeGScore + Graph.CostEstimate(Neighbor, End); if (!Open.Contains(Neighbor)) { Open.Add(Neighbor); } } } } // If we can't path, then we give up Debug.Log("Camera Pathing failure!"); return(new List <CameraGraphPortalNode>()); }