0

I am currently working on a project involving procedurally generated underwater terrain using 3d Simplex noise and the marching cubes algorithm. Right now I have made my character model as well as generated the terrain mesh. Since the project is based in the water, I wanted to add fish and other entities to make it more realistic. I have read the basic on boids and have already created the program for entities to interact in a 3d space. However the problem I have been stuck on is having the entities interacting with obstacles. The method used in this video is by casting rays in a sphere around the entity and detecting whether or not a ray is intersecting with the obstacle. My idea is to check each point according to an interval along each ray until I detect a point that is intersecting a triangular face. I already have the code to check whether or not a 3d point is on a triangular face so my question is would I have to loop through each point along each ray in order calculate whether it is intersecting a triangle or is there a better way for me to do it?

Example code

for(Ray ray : rayList){
    for(float j = 0; j<raylength; j+= rayinterval){
        if(this.checkCollision(ray.getPos(raylenth))){
             break;
        }else if(j == raylenth-1){
             return ray.getAngles();
        }
    }
}
    public boolean checkCollision(Vector3f position){
        if(terrain != null){
            for(int i = 0; i<terrain.getVertices().length; i+=9){
                Vector3f vertex1 = new Vector3f(terrain.getVertices()[i],terrain.getVertices()[i+1],terrain.getVertices()[i+2]);
                Vector3f vertex2 = new Vector3f(terrain.getVertices()[i+3],terrain.getVertices()[i+4],terrain.getVertices()[i+5]);
                Vector3f vertex3 = new Vector3f(terrain.getVertices()[i+6],terrain.getVertices()[i+7],terrain.getVertices()[i+8]);
                
                if(inTriangle(position, vertex1, vertex2, vertex3)){
                    return true;
                }
            }
        }
        return false;
    }
    public UVList getUV(Vector3f a, Vector3f b, Vector3f c){

        //First, calculate the unit normal vector (cross product).
        Vector3f ba = b.subtract(a);
        Vector3f ca = c.subtract(a);
        Vector3f nn = ba.cross(ca);
        float unitVector = (float) Math.sqrt(nn.x*nn.x+nn.y*nn.y+nn.z*nn.z);
        Vector3f n = nn.divide(unitVector);

        //Calculate the signed distance from origin (dot product).
        float d = n.dot(a);

        //Calculate the three possible divisors.
        float div_xy = a.x*(c.y-b.y) + b.x*(a.y-c.y) + c.x*(b.y-a.y);
        float div_xz = a.x*(c.z-b.z) + b.x*(a.z-c.z) + c.x*(b.z-a.z);
        float div_yz = a.y*(c.z-b.z) + b.y*(a.z-c.z) + c.y*(b.z-a.z);
        float abs_xy = Math.abs(div_xy);
        float abs_xz = Math.abs(div_xz);
        float abs_yz = Math.abs(div_yz);
        Vector3f u,v;
        float u0,v0;
        if(abs_xy >= abs_xz && abs_xy >= abs_yz){
            //d_xy has the largest absolute value; using xy plane
            u = new Vector3f((a.y-c.y)/div_xy, (c.x-a.x)/div_xy, 0);
            v = new Vector3f((b.y-a.y)/div_xy, (a.x-b.x)/div_xy, 0);
            u0 = (a.x*c.y - a.y*c.x)/div_xy;
            v0 = (a.y*b.x - a.x*b.y)/div_xy;
        }else if( abs_xz >= abs_xy && abs_xz >= abs_yz){
            //d_xz has the largest absolute value; using xz plane
            u = new Vector3f((a.z-c.z)/div_xz, 0, (c.x-a.x)/div_xz);
            v = new Vector3f((b.z-a.z)/div_xz, 0, (a.x-b.x)/div_xz);
            u0 = (a.x*c.z - a.z*c.x)/div_xz;
            v0 = (a.z*b.x - a.x*b.z)/div_xz;
        }else{
            //d_yz has the largest absolute value; using yz plane
            u = new Vector3f(0, (a.z-c.z)/div_yz, (c.y-a.y)/div_yz);
            v = new Vector3f(0, (b.z-a.z)/div_yz, (a.y-b.y)/div_yz);
            u0 = (a.y*c.z - a.z*c.y)/div_yz;
            v0 = (a.z*b.y - a.y*b.z)/div_yz;
        }
        return new UVList(u0,v0,u,v,d,n);
    }
    public class UVList{
        Vector3f u,v,n;
        float u0, v0,d;
        public UVList(float u0, float v0, Vector3f u, Vector3f v,float d,Vector3f n){
            this.u = u;
            this.v = v;
            this.u0 = u0;
            this.v0 = v0;
            this.d = d;
            this.n = n;
        }
        public Vector3f getN(){
            return n;
        }
        public Vector3f getU(){
            return u;
        }
        public Vector3f getV(){
            return v;
        }
        public float getU0(){
            return u0;
        }
        public float getV0(){
            return v0;
        }
        public float getD(){
            return d;
        }
    }
    public boolean inTriangle(Vector3f p, Vector3f v1, Vector3f v2, Vector3f v3){
        float ellipse = 1.5f;
        
        UVList uv = getUV(v1,v2,v3);
        
        
        if(Math.abs(p.dot(uv.getN())-uv.getD()) >= ellipse){
            return false;
        }
        
        float u = p.dot(uv.getU())+uv.getU0();
        
        float v = p.dot(uv.getV())+uv.getV0();

        if(u < 0 || u > 1){
            return false;
        }
        

        if(v < 0 || v > 1){
            return false;
        }
        
        if(u+v > 1){
            return false;
        }
        
        return true;
    }

2d diagram

2D diagram

And here is the terrain I want the entities to detect:

terrain enter image description here I am fairly new to 3d engines and collision so my code might not be the best.

Ethan Ma
  • 69
  • 3
  • 7
  • @httpdigest is there anyway for the method to work with linesegments instead of rays since I want the collision bounds to be in a sphere? – Ethan Ma Aug 18 '20 at 02:49
  • 1
    @httpdigest I think I get it now. I can just use parameter t to check if the distance between the ray at the intersection is within a certain distance. Thanks for your help! – Ethan Ma Aug 19 '20 at 02:54

0 Answers0