Check object in field of sight

Hi,

I have a planets with some agents (pursuers and evaders) going around.
I want to identify all the triangles in the mesh of the planet that are actually visible to the pursuers.
All the pursuers have a direction and a certain angle for the field of sight (center of it being the direction)

Hence I’m trying to understand what would be the best way to check if an object (so the triangle) is inside, even if partially, into the field of sight of an agent.

What I’ve tried so far is to cast 3 rays from the position of the pursuer to the vertices of each triangle, and if it is not colliding with planet, it checks if the rays are in the field of sight of the pursuer, if at least one was inside, then it would be visible.

But this does not seem to work properly, plus, if only one internal part of the triangle is visible it won’t work at all.

Screenshot
(the yellow part is the part visible, is clearly not working)

That’s what I have got so far

(The vertex from the graph are the triangles of the outer green mesh that you can see in the screenshot)

public void calculateSafeTriangles(){
    for(Agent agent: agents){  //loop through all the agent
        if(!(agent instanceof Pursuer)) //if it is not a pursuer just continue with the next agent
            continue;
        
        Pursuer current = (Pursuer) agent;

        for(Vertex v: graph.getVertices()){  // Here vertex v refers to a vertex of the graph, not a vertex of the mesh, it stores a triangle of the mesh and its info.
                v.setSafe(true); //v is set initially to true, meaning that the triangle is not visible from the pursuer

//Here it casts rays to the 3 vertices of the triangle
                    Ray ray1 = new Ray(current.getNodeAgent().getWorldTranslation(),v.getTriangle().get1().subtract(current.getNodeAgent().getWorldTranslation()).normalizeLocal()); 
                    Ray ray2 = new Ray(current.getNodeAgent().getWorldTranslation(),v.getTriangle().get2().subtract(current.getNodeAgent().getWorldTranslation()).normalizeLocal());
                    Ray ray3 = new Ray(current.getNodeAgent().getWorldTranslation(),v.getTriangle().get3().subtract(current.getNodeAgent().getWorldTranslation()).normalizeLocal());
                                        
//And it checks the collision with the planet
                    CollisionResults results1 = new CollisionResults();                 
                    planet.getPlanet().collideWith(ray1, results1);
                
                CollisionResults results2= new CollisionResults();                                    
                planet.getPlanet().collideWith(ray2, results2);
                
                CollisionResults results3 = new CollisionResults();
                planet.getPlanet().collideWith(ray3, results3);
           
//if it does not collide with the planet, it means that could be visible 
// and so checks if the ray is inside the field of sight of the pursuer
                if(results1.size()==0){ 
                    if(checkInSight(current, v.getTriangle().get1())){ 
                        v.setUnsafe();
                        return;
                    }
                    
                } if(results2.size()==0){
                    if(checkInSight(current, v.getTriangle().get2())){
                        v.setUnsafe();
                        return;
                    }
                } if(results3.size()==0){
                    if(checkInSight(current, v.getTriangle().get3())){
                        v.setUnsafe();
                        return;
                    }
                }
        
        }
    }
}


 protected boolean checkInSight(Pursuer pursuer, Vector3f point){
        Vector3f cube2ToOrigin = point.add(pursuer.getNodeAgent().getWorldTranslation().mult(-1));
        Vector3f direction = pursuer.getNodeAgent().getWorldRotation().getRotationColumn(2);
        
        double angle = getAngle(direction, cube2ToOrigin);
        
        if (angle > Math.toRadians(pursuer.getField_of_sight()/2)) {
            return false;
        }
        return true;
}
 
 protected double getAngle(Vector3f v1, Vector3f v2){
    double dot = v1.dot(v2);
    double lenghtprod = v1.length()*v2.length();
    double result = dot / lenghtprod;
    return Math.acos(result);      

  
}

Any hint?

1 Like

I’m not familiar with this kind of thing but…
It looks to me like you are looping through all the vertices of the sphere. For each one you find a triangle it belongs to and check the three vertices of that triangle. The smoothness of the sphere in your screenshot suggests that the triangles are sharing vertices. If I’m correct, you are relying on getTriangle() over all the vertices to cover all the triangles. As each vertex belongs to six different triangles it seems entirely plausible that triangles could be missed this way.

In any case, it would be helpful if your Ray constructor lines were more readable and if you included all the relevant code, e.g. your Vertex.setUnsafe() method.

1 Like

It is a bit unclear indeed.
What I do basically is creating a graph in which each vertex object contains a triangle of the mesh and its info.
Hence when I use a vertex v in the code I’m refering to the vertex of the graph (so the triangle of the mesh) and not to a vertex of the mesh itself.

Anyway I think there is nothing much to add that is relevant, the setUnsafe just means that the triangle is visible and hence is not good for an evader to stay on.

I’ll add some comments to make everything more clear. Anyway thanks :slight_smile:

1 Like

Imagine the view cone of the player as a pyramid inside the world. You want to find out if the world position of the vertex is inside the pyramid.

You can do that by shooting a random ray from that location, theres two possible results that tell you if the position is inside or outside the pyramid.

a) If it does not hit one of the outer planes of the pyramid or it hits them twice or more its outside of the pyramid.

b) If it hits one of the outer planes once its inside the pyramid.

Heres some code I used a while ago to do this, it was used to find the selected objects of “rubber band selections”:

t = cam position (pyramid "top")
b1-4 = pyramid "base" corners
    public boolean isVectorInPyramid(Vector3f v){
        int cnt = 0;
        Ray ray = new Ray(v, rndVec());

        //seiten
        if ( ray.intersect(t, b1, b2) ) cnt++;
        if ( ray.intersect(t, b2, b3) ) cnt++;
        if (cnt>1) return false;
        if ( ray.intersect(t, b3, b4) ) cnt++;
        if (cnt>1) return false;
        if ( ray.intersect(t, b4, b1) ) cnt++;
        if (cnt>1) return false;

        //unterseite
        if ( ray.intersect(b1, b2, b3) ) cnt++;
        if (cnt>1) return false;
        if ( ray.intersect(b1, b3, b4) ) cnt++;
        if (cnt>1) return false;
        if (cnt==1)
            return true;
        else //->(cnt==0 || cnt>1)
            return false;
    }
1 Like

I have one question

This will only tell me whether a vertex is inside the pyramid, not if it is actually visible (e.g. there is an obstacle in front of it) is that correct? If it is, then how do you check if there is an obstacle (in my case the planet itself)?

1 Like

Well you do a normal ray test from the player.

1 Like

I tried using the pyramid, but apparently it never return true.
Am I doing it correctly?
(The method for building the pyramid works good, I tried to display it and it looks perfect)

 public boolean insidePyramid(Vector3f object, Pursuer pursuer){
    
   Vector3f eyePosition = pursuer.getNodeAgent().getWorldTranslation();
   Vector3f direction = pursuer.getNodeAgent().getLocalRotation().getRotationColumn(2);
   
   Ray rayPlan = new Ray(planet.getPlanet().getLocalTranslation(),eyePosition.subtract(planet.getPlanet().getLocalTranslation()).normalizeLocal());
   CollisionResults results = new CollisionResults();
   planet.getPlanet().collideWith(rayPlan, results);
   
   Vector3f normalPoint = results.getFarthestCollision().getContactNormal(); 
    
   Vector3f[] points = buildPyramid(eyePosition,direction,normalPoint); 
    
   int cnt = 0;
   Ray ray = new Ray(object, Vector3f.UNIT_Y);
 
    if ( ray.intersectWherePlanar(object, points[4], points[6], null) ) cnt++;
    if ( ray.intersectWherePlanar(object, points[4], points[5], null) ) cnt++;
    if (cnt>1) return false;
    if ( ray.intersectWherePlanar(object, points[5], points[7], null) ) cnt++;
    if (cnt>1) return false;
    if ( ray.intersectWherePlanar(object, points[7], points[6], null) ) cnt++;
    if (cnt>1) return false;

    
    if ( ray.intersectWherePlanar(points[4], points[5], points[6], null) ) cnt++;
    if (cnt>1) return false;
    if ( ray.intersectWherePlanar(points[5], points[7], points[6], null) ) cnt++;
    if (cnt>1) return false;
    if (cnt==1)
        return true;
    else //->(cnt==0 || cnt>1)
        return false;
    
}
 
public Vector3f[] buildPyramid(Vector3f eyePosition,Vector3f direction,Vector3f upVector){
     
     direction.normalize();
     
     Vector3f rightVector = direction.cross(upVector);
     float nearDistance = 10;
     float farDistance = 1000;
     float fov = 110;
     float aspectRatio = 16/12;
     
     float Hnear = (float) (2 * Math.tan(fov/2) * nearDistance);
     float Wnear = Hnear*aspectRatio;
     
     float Hfar = (float) (2 * Math.tan(fov / 2) * farDistance);
     float Wfar = Hfar * aspectRatio;
     
     Vector3f Cnear = eyePosition.add(direction.mult(nearDistance));
     Vector3f Cfar = eyePosition.add(direction.mult(farDistance));
     
     Vector3f NTL,NTR,NBL,NBR,FTL,FTR,FBL,FBR;
     
     NTL = Cnear.add(upVector.mult(Hnear/2)).subtract(rightVector.mult(Wnear/2));
     NTR = Cnear.add(upVector.mult(Hnear/2)).add(rightVector.mult(Wnear/2));
     NBL = Cnear.subtract(upVector.mult(Hnear/2)).subtract(rightVector.mult(Wnear/2));
     NBR = Cnear.subtract(upVector.mult(Hnear/2)).add(rightVector.mult(Wnear/2));
     
     FTL = Cfar.add(upVector.mult(Hfar/2)).subtract(rightVector.mult(Wfar/2));
     FTR = Cfar.add(upVector.mult(Hfar/2)).add(rightVector.mult(Wfar/2));
     FBL = Cfar.subtract(upVector.mult(Hfar/2)).subtract(rightVector.mult(Wfar/2));
     FBR = Cfar.subtract(upVector.mult(Hfar/2)).add(rightVector.mult(Wfar/2));
     
     Vector3f [] v = {NTL,NTR,NBL,NBR,
                      FTL,FTR,FBL,FBR};
     return v;
    
     
 }
1 Like

Isn’t there a method camera.isInView(spatial) or something like that? I never got it to work properly though.

1 Like