0
\$\begingroup\$

I have been trying to program collision response between two objects (with AABBs) and the response is not what I except. Instead of bouncing off (reflective to the collision normal) each other the stick and jitter together for an instant and bounce off in unexpected directions.

When I debugged the code, I realised the two objects collide up to 8 times before there is a response, all giving different normals, so I put a boolean to stop the objects from re-colliding if the same 2 objects have already collided in the previous frame and it seems to work. Even though it may be fixed, I want to know why the 2 objects collide up to 8 times before there is a response, and not just once?

Code before editing:

Main physics class:

import java.util.ArrayList;
import java.util.List;

import org.joml.Vector3f;

import engine.physics.forces.Force;
import engine.physics.primitives.CollisionData;
import engine.physics.primitives.CollisionResponse;
import engine.physics.rigidbody.RigidBody;
import engine.terrains.Terrain;

public class PhysicsEngine {

public List<RigidBody> rigidBodies = new ArrayList<RigidBody>();

public List<CollisionResponse> collisionResponse = new ArrayList<CollisionResponse>();

public Force gravity = new Force(new Vector3f(0, -1f, 0), 9.8f);

public PhysicsEngine() {
    
}


public void update(List<Terrain> terrains, float dt) {
    
    for(int i = 0; i < rigidBodies.size(); i++) {
        
        handleObjectCollisions(i, rigidBodies.get(i));
        
        handleTerrainCollisions(rigidBodies.get(i), terrains);
        
        rigidBodies.get(i).updatePhysics(dt);
        
    }
    
}

public void handleObjectCollisions(int i, RigidBody body) {
    for(int j = i + 1; j < rigidBodies.size(); j++) {
        
        CollisionData collisionData = body.getCollider().getBounds().get(0).intersectsAABB(rigidBodies.get(j).getCollider().getBounds().get(0));
        
        if(collisionData.isColliding && !(body.previousCollision == rigidBodies.get(j))) {
            
            collisionResponse.add(new CollisionResponse(body, rigidBodies.get(j), collisionData.collisionNormal));
            
        }
        
    }
    
    for(CollisionResponse response: collisionResponse) {
        response.doCollisionResponse();
    }
    
    if(!collisionResponse.isEmpty()) {
        collisionResponse.clear();
    }
    
}

public void handleTerrainCollisions(RigidBody body, List<Terrain> terrains) {
    for(Terrain terrain: terrains) {
        if(terrain.isWithinTerrainArea(body.getTransform().getPosition().x, body.getTransform().getPosition().z)) {
            
            float terrainHeight = terrain.getHeight(body.getTransform().getPosition().x, body.getTransform().getPosition().z) + body.getCollider().getBounds().get(0).getRadii().y;
            body.addToNetForce(gravity.getMagnitudeAndDirection().mul(body.getMass()));
            
            if(body.getTransform().getPosition().y > terrainHeight + 0.1f) {
                
                if(!body.isInAir()) {
                    body.setInAir(true);
                }
                
                return;
                
                
            }else {
                
                if(body.isInAir()) {
                    body.setInAir(false);
                    
                    if(Math.abs(body.velocity.getMagnitudeAndDirection().y) >= 1f) {
                        body.velocity.setDirection(body.velocity.getDirection().reflect(terrain.getNormal(body.getTransform().getPosition().x, body.getTransform().getPosition().z)));
                        body.velocity.setVector(body.velocity.getMagnitudeAndDirection().mul(0.4f));
                    }
                    
                }else {
                    body.addToNetForce(gravity.getMagnitudeAndDirection().negate().mul(gravity.getMagnitudeAndDirection().dot(terrain.getNormal(body.getTransform().getPosition().x, body.getTransform().getPosition().z))));
                    
                    if(body.velocity.getMagnitude() < 2f && body.velocity.getMagnitude() > -2f) {
                        
                        body.setIgnoreVelocity(true);
                        
                    }else {
                        
                        body.getNetForce().addVector(body.getNetForce().getMagnitudeAndDirection().mul((float) -Math.abs(Math.sin(body.getNetForce().getMagnitude()))));
                        
                    }
                    
                    body.getTransform().getPosition().y = terrainHeight;
                    
                }
                
                
                
            }
            
            
            break;
        }
    }
}

private void transferMomentum(RigidBody a, RigidBody b) {
    Vector3f momentumA = a.getMomemtum();
    
    a.setMomemtum(b.getMomemtum());
    b.setMomemtum(momentumA);
}

public void addRigidBody(RigidBody body) {
    rigidBodies.add(body);
}
}

AABB intersect function:

    public CollisionData intersectsAABB(AABB other) {
    
    Vector3f thisWorldCenter = new Vector3f(getWorldMax().x - radii.x, getWorldMax().y - radii.y, getWorldMax().z - radii.z);
    Vector3f otherWorldCenter = new Vector3f(other.getWorldMax().x - other.getRadii().x, other.getWorldMax().y - other.getRadii().y, other.getWorldMax().z - other.getRadii().z);
    
    //check the X axis
    if(Math.abs(thisWorldCenter.x - otherWorldCenter.x) < radii.x + other.getRadii().x)
       {
        
        float sizeX = (float) Math.abs(Math.abs(thisWorldCenter.x - otherWorldCenter.x) - (radii.x + other.getRadii().x));
        
          //check the Y axis
          if(Math.abs(thisWorldCenter.y - otherWorldCenter.y) < radii.y + other.getRadii().y)
          {
              
              float sizeY = (float) Math.abs(Math.abs(thisWorldCenter.y - otherWorldCenter.y) - (radii.y + other.getRadii().y));
              
              //check the Z axis
              if(Math.abs(thisWorldCenter.z - otherWorldCenter.z) < radii.z + other.getRadii().z)
              {
                 
                  float sizeZ = (float) Math.abs(Math.abs(thisWorldCenter.z - otherWorldCenter.z) - (radii.z + other.getRadii().z));
                 
                  float sx = thisWorldCenter.x < otherWorldCenter.x ? -1.0f : 1.0f;
                  float sy = thisWorldCenter.y < otherWorldCenter.y ? -1.0f : 1.0f;
                  float sz = thisWorldCenter.z < otherWorldCenter.z ? -1.0f : 1.0f;
                 
                  Vector3f firstCollisionNorm;
                 
                  if(sizeX <= sizeY && sizeX <= sizeZ) {
                     
                      firstCollisionNorm = new Vector3f(sx, 0.0f, 0.0f);
                     
                  }else if(sizeY <= sizeZ) {
                     
                      firstCollisionNorm = new Vector3f(0.0f, sy, 0.0f);
                     
                  }else {
                     
                      firstCollisionNorm = new Vector3f(0.0f, 0.0f, sz);
                  }
                 
                 return new CollisionData(true, firstCollisionNorm);
              }
          }
       }

       return new CollisionData();
       
}

CollisionData Class:

public class CollisionData {

public boolean isColliding = false;
public Vector3f collisionNormal = new Vector3f(0, 0, 0);

public CollisionData() {
    
}

public CollisionData(boolean isColliding, Vector3f collisionNormal) {
    
    this.isColliding = isColliding;
    this.collisionNormal = collisionNormal;
    
    System.out.println(collisionNormal); 
    
}

}

Collision Response:

public class CollisionResponse {

private RigidBody a;
private RigidBody b;

private Vector3f normal;

public CollisionResponse(RigidBody a, RigidBody b, Vector3f normal) {
    this.a = a;
    this.b = b;
    this.normal = normal;
}

public void doCollisionResponse(){
    a.velocity.setVector(a.velocity.getMagnitudeAndDirection().reflect(normal));
    b.velocity.setVector(b.velocity.getMagnitudeAndDirection().reflect(normal));
}

}

Code Edited:

    public void handleObjectCollisions(int i, RigidBody body) {
    for(int j = i + 1; j < rigidBodies.size(); j++) {
        
        CollisionData collisionData = body.getCollider().getBounds().get(0).intersectsAABB(rigidBodies.get(j).getCollider().getBounds().get(0));
        
        if(collisionData.isColliding && !(body.previousCollision == rigidBodies.get(j))) {
            
            collisionResponse.add(new CollisionResponse(body, rigidBodies.get(j), collisionData.collisionNormal));
            
            body.previousCollision = rigidBodies.get(j);
            rigidBodies.get(j).previousCollision = body;
            
        }else if(body.previousCollision == rigidBodies.get(j)) {
            body.previousCollision = null;
            rigidBodies.get(j).previousCollision = null;
        }
        
    }
    
    for(CollisionResponse response: collisionResponse) {
        response.doCollisionResponse();
    }
    
    if(!collisionResponse.isEmpty()) {
        collisionResponse.clear();
    }
    
}

In the RigidBody class, I added another RigidBody var called 'previousCollision' as seen in the edited method above from the physics engine class. This var will store the previous rigidbody that was collided with and will be cleared after 2 frames (again as seen in the method above). This does fix the problem, but why did I need to do it?

Also, I know the rigidBodies are colliding more that once, because this line System.out.println(collisionNormal) from the CollisionData class prints out 8 times in the console even though I only have 2 rigidbodies in the arraylist in the physics engine class.

\$\endgroup\$

0

You must log in to answer this question.

Browse other questions tagged .