# Why does this implementation of aabb-sphere collision ghost collide and how can I fix it

I am trying to make a collision detection system and the aabb-sphere collision isn't working. Here is my code.

This method checks to see which whether I should collide with AABB-AABB, Sphere-Sphere, or AABB-Sphere.

public static boolean collides(BoundingBox box,BoundingBox other) {
if(box instanceof AABB && other instanceof AABB) {
return collides((AABB) box,(AABB) other);
}
if(box instanceof Sphere && other instanceof Sphere) {
return collides((Sphere) box,(Sphere) other);
}
if(box instanceof AABB && other instanceof Sphere) {
return collides((AABB) box,(Sphere) other);
}
if(box instanceof Sphere && other instanceof AABB) {
return collides((Sphere) box,(AABB) other);
}
return false;

This method does the actual colliding.

public static boolean collides(AABB other,Sphere sphere) {
Vector3f p = closestPointToAabb(other, sphere.getPos());
Vector3f v = Vector3f.sub(p, sphere.getPos(), null);
float dist = Vector3f.dot(v, v);
return collision;

};

This method calculates the point closest to aabb.

public static Vector3f closestPointToAabb(AABB aabb,final Vector3f point) {
if(point.x > aabb.getMax().x ||  point.y > aabb.getMax().y ||point.z > aabb.getMax().z ) {
return aabb.getMax();
}else if(point.x < aabb.getMin().x ||  point.y < aabb.getMin().y ||point.z < aabb.getMin().z ) {
return aabb.getMin();
}
return point;
}

This method does all the logic.

public static void updateObjects() {
for(int collider = 1; collider < axisList.size(); collider++) {
BoundingBox colliderBox = axisList.get(collider);

for(int collidee = 0; collidee < collider; collidee++) {

BoundingBox collideeBox = axisList.get(collidee);

if((colliderBox.getStatic() && collideeBox.getStatic()) && !(colliderBox instanceof Player || collideeBox instanceof Player)) {
continue;
}
if(BoundingBox.collides(colliderBox, collideeBox)) {

System.out.println("Collision with " + colliderBox.getName() +  " and " + collideeBox.getName());

}

}
}
}
}

Edit:

It seems that the collision is working but the Bounding Boxes collide in random places. I think it is a problem in how I am specifying my min and max and the sphere's centre and radius.

Here is how I specify it.

public static void addObject(Entity entity) {
if(entity instanceof Player) {
Sphere sphere = new Sphere(entity.getModel().getMin(),entity.getModel().getMax(),entity.getPosition(),false,entity.getName());
entity.setBox(sphere);
return;
}
}

And here is three methods(The code is pretty bad but I will fix it).

removeUnusedVertices(vertices);
float[] verticesArray = new float[vertices.size() * 3];
float[] texturesArray = new float[vertices.size() * 2];
float[] normalsArray = new float[vertices.size() * 3];
float furthest = convertDataToArrays(vertices, textures, normals, verticesArray,
texturesArray, normalsArray);
int[] indicesArray = convertIndicesListToArray(indices);
Vector3f min = min(vertices);
Vector3f max = max(vertices);
return model;

private static Vector3f min(List<Vertex> vertices) {
List<Float>  xArray= new ArrayList<>();
List<Float> yArray = new ArrayList<>();
List<Float> zArray = new ArrayList<>();
for(Vertex vertex : vertices) {
}
return new Vector3f(Collections.min(xArray),Collections.min(yArray),Collections.min(zArray));
}

private static Vector3f max(List<Vertex> vertices) {
List<Float>  xArray= new ArrayList<>();
List<Float> yArray = new ArrayList<>();
List<Float> zArray = new ArrayList<>();
for(Vertex vertex : vertices) {
}
return new Vector3f(Collections.max(xArray),Collections.max(yArray),Collections.max(zArray));
}

My entity intiialization

box = new AABB(getModel().getMin(),getModel().getMax(),position,name,staticObject);

AABB and sphere:

public AABB(Vector3f min,Vector3f max,Vector3f pos,String name,boolean staticObject) {
this.min = min;
this.max = max;
this.name = name;
this.staticObject = staticObject;
this.pos = pos;
}

public Sphere(Vector3f min,Vector3f max,Vector3f pos,boolean staticObject,String name) {
center = Vector3f.sub(max, min,null);
this.pos = pos;
this.staticObject = staticObject;
this.name = name;
}

Edit Again:

I debugged my code again and found that when I make contact with an aabb it reports that I'm colliding with all the aabb's in the scene.

I have no idea what could be causing this.

• Remember to include a description of the test case that leads to incorrect behaviour, so users don't have to guess what data to try to reproduce the issue. Apr 3 '18 at 19:19
• @DMGregory I edited my post to include a description of the problem. Apr 4 '18 at 8:36

Without knowing more of what error conditions your running into I only have one guess at what is going wrong. It's your "closestPointToAabb()". It looks like your using ||(or) to find your max and min boundaries, instead of checking them each individually. The psuedocode for what you're doing now looks like this:

closestPointToAabb(aabb, point)
{
// max
if( point.x > aabb.x ) return aabb.max();
if( point.y > aabb.y ) return aabb.max();
if( point.z > aabb.z ) return aabb.max();
// min
if( point.x < aabb.x ) return aabb.min();
if( point.y < aabb.y ) return aabb.min();
if( point.z < aabb.z ) return aabb.min();
}

By using ||(or) you're effectively returning the max if any of those conditions are true. Meaning that you were either returning 1 of 3 things:

1) A point inside the aabb
2) The max corner of the aabb
3) The min corner of the aabb

What I think you want to be doing is building a new point based on each spacial component individually. The psuedocode for that would be:

closestPointToAabb(aabb, point)
{
Point p;
// for each coordinate axis clamp it to the bounds of the aabb
for( axis a in xyz ){
float v = point[a]
if( v < aabb.min[a] ) v = aabb.min[a];
if( v > aabb.max[a] ) v = aabb.max[a];
p[a] = v;
}
return p;
}

This will be able to return to you any of the corners of your aabb, as well as a point inside of your aabb, AND a point along any of the faces of your aabb.

This is the best answer I could come up with using the information you've given us, but hopefully its a good starting point. For some more information on AABB -> Sphere collision you can check out another of my answers here.

= EDIT =

I think your right about issues in building your bounding volumes. The extra code is helpful but it's still a little hard to tell without knowing how your using them, and what issues your running into. Additionally I still think that "closestPointToAabb()" needs to be addressed, but since that's covered above here is some help with building the volumes.

AABB

I'm not sure how these variables are being updated in the AABB code, so these are not recommended fixes, just some things to check for:

1) Are 'min' and 'max' the worldspace positions of those corners? Because if so you'll need to update them along with the position whenever it moves.

2) Are 'min' and 'max' a distance away from the center to those corners? If so then they shouldn't change, and instead just get added/subtracted from the position when you need to check against them. Otherwise your box will be centered at the origin(0,0,0). Additionally if they're a distance from center, then you really only need to store one of them as a "radius" and you can add/sub that from the position when you use it.

SPHERE

It looks like this setup has a few issues:

1) What is the difference between 'center' and 'pos'? Do they do different things? From an outsider perspective it seems like only one of them is necessary.

2) I'm not sure what values you're passing in as 'min' and 'max' but when you dot two non-normalized vectors you get some large numbers. I think you may want to change your radius calculation to:

// psuedocode
Vector3f diameterVector = max - min;
Vector3f radiusVector   = diameterVector / 2;

This gets the distance from min to max which is the diameter. And then half the diameter is the radius. (r=d/2)

3) If I misread it and the 'center' and 'pos' do actually have different functionality then you can calculate your center by adding the 'radiusVector' to the min.

// same radius psuedocode as above
Vector3f diameterVector = max - min;
Vector3f radiusVector   = diameterVector / 2;