private IEnumerator ComputeVisibilities() { Debug.Log("Computing Visibilities..."); Debug.Assert(!IsInLayerMask(gameObject, CameraOcclusion), "You can't have the camera graph block the camera graph!"); // Gross, n! for (int i = 0; i < Spheres.Count; ++i) { CameraGraphSphereNode A = Spheres[i] as CameraGraphSphereNode; for (int j = i; j < Spheres.Count; ++j) { if (i == j) { continue; } CameraGraphSphereNode B = Spheres[j] as CameraGraphSphereNode; A.ComputeVisibility(B); } // Yield to draw if (ShouldOperationYield()) { yield return(new WaitForEndOfFrame()); } } }
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 bool Intersects(CameraGraphSphereNode other) { // Get the distance Vector3 dist = transform.position - other.transform.position; float CombRad = Radius + other.Radius; return(dist.sqrMagnitude < CombRad * CombRad); }
// Instantiates a child node at a position, but doesn't set much else private CameraGraphSphereNode InstantiateNode(Vector3 Position, int id) { GameObject Node = new GameObject("Node " + id.ToString()); Node.transform.SetParent(transform); Node.transform.position = Position; CameraGraphSphereNode Sphere = Node.AddComponent <CameraGraphSphereNode>(); Sphere.Graph = this; return(Sphere); }
public void ComputeVisibility(CameraGraphSphereNode Target) { float VisibleHits = 0; int NumSamples = Graph.VisibilityCastSamples; // Use MonteCarlo Sampling by raycasting a bunch randomly to see how many can see the target Vector3 Direction = (Target.transform.position - transform.position); float MaxCastDist = Direction.magnitude; Direction.Normalize(); for (int CastNum = 0; CastNum < NumSamples; ++CastNum) { // Compute the random offset Vector3 RandomOffset = Random.insideUnitSphere * Radius; // Cast a ray towards the target Ray R = new Ray(transform.position + RandomOffset, Direction); RaycastHit Info; if (Physics.Raycast(R, out Info, MaxCastDist, Graph.CameraOcclusion) == true) { // The ray is blocked // Snazzy DebugDraw Debug.DrawLine(transform.position + RandomOffset, transform.position + RandomOffset + Direction * Info.distance, Color.red * 0.5f, 1.0f); } else { // The ray is not blocked VisibleHits++; // Snazzy DebugDraw //Debug.DrawLine(transform.position + RandomOffset, // transform.position + RandomOffset + Direction * MaxCastDist, Color.green * 0.1f, 0.25f, true); } } // Our visibility value is the % of samples that hit VisibilityValues.Add(Target, VisibleHits / NumSamples); // Visibility is two way Target.VisibilityValues.Add(this, VisibleHits / NumSamples); }
public void Connect(CameraGraphSphereNode NodeA, CameraGraphSphereNode NodeB) { A = NodeA; B = NodeB; Vector3 dir = B.transform.position - A.transform.position; float dist = dir.magnitude; // Compute the intersection midpoint float Penetration = Mathf.Abs(dist - (A.Radius + B.Radius)); float IntersectionDist = A.Radius - Penetration * 0.5f; transform.position = A.transform.position + dir.normalized * IntersectionDist; // Use pythagorean theorem to get our radius Radius = Mathf.Sqrt(A.Radius * A.Radius - IntersectionDist * IntersectionDist); // And our normal is just along the direction Normal = dir.normalized; }
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); }
public CameraGraphSphereNode GetClosestSphere(Vector3 Position) { float BestSqrDist = Mathf.Infinity; CameraGraphSphereNode Best = Spheres[0] as CameraGraphSphereNode; // This is unoptimized foreach (CameraGraphSphereNode Sphere in Spheres) { float SqrDist = (Position - Sphere.transform.position).sqrMagnitude; if (SqrDist < BestSqrDist) { BestSqrDist = SqrDist; Best = Sphere; } // Early out for being inside a sphere if (SqrDist < Sphere.Radius * Sphere.Radius) { return(Sphere); } } return(Best); }
IEnumerator GenerateSpheres() { Debug.Log("Generating Camera Space Spheres ..."); // Find a good sphere size // Make sure our bounds have updated positions CameraBoundary Bounds = GetComponent <CameraBoundary>(); Bounds.CalcPositons(); // Compute how much we move each step between spheres float Delta = 2f * (MaxSphereSize - Overlap); Debug.Assert(2f * (MinSphereSize - Overlap) > 1f, "Gap between spheres can become very small!"); // The sum unit offsets between nodes Vector3 Offset = new Vector3(Delta, Delta, Delta); Debug.Assert(Delta > 0f); // Compute our starting and ending sphere positions Vector3 StartSpherePos = Bounds.FrontTopLeft; Vector3 EndSpherePos = Bounds.BackBottomRight + 0.5f * Offset; Debug.Log("Placing Nodes from " + StartSpherePos + " to " + EndSpherePos); // Loop through all space in the camera bounds // and plop a sphere node down Vector3 CurrentSpherePos = StartSpherePos; int id = 0; // Loop on X for (CurrentSpherePos.x = StartSpherePos.x; // (Redundant) CurrentSpherePos.x <= EndSpherePos.x; CurrentSpherePos.x += Delta) { // Loop on Z for (CurrentSpherePos.z = StartSpherePos.z; CurrentSpherePos.z <= EndSpherePos.z; CurrentSpherePos.z += Delta) { // Loop on Y for (CurrentSpherePos.y = StartSpherePos.y; CurrentSpherePos.y >= EndSpherePos.y; CurrentSpherePos.y -= Delta) // Note the subtraction here -- we want our spheres to be connected vertically { id++; // Debug.Log("Adding node " + " at " + CurrentSpherePos); float Radius = CheckNodeSize(CurrentSpherePos, MaxSphereSize); if (Radius != 0f) { CameraGraphSphereNode Node = InstantiateNode(CurrentSpherePos, id); Node.Radius = Radius; Spheres.Add(Node); if (ShouldOperationYield()) { yield return(new WaitForEndOfFrame()); } // Compute how much we move each step between spheres Delta = 2f * (Radius - Overlap); } } } } Debug.Log("Created " + Spheres.Count + " Spheres"); yield return(null); }
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>()); }