Пример #1
0
    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());
            }
        }
    }
Пример #2
0
    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));
    }
Пример #3
0
    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);
    }
Пример #4
0
    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);
    }
Пример #6
0
    // 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;
    }
Пример #9
0
    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);
    }
Пример #10
0
    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);
    }
Пример #11
0
    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);
    }
Пример #12
0
    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>());
    }