# 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);
boolean collision = dist <= sphere.getRadius() * sphere.getRadius();
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());
collisionList.add(new Collision(colliderBox,collideeBox));

}

}
}
}
}


Could anyone please help me. Also How should I specify my min and max and the sphere's centre and radius.

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());
axisList.add(sphere);
entity.setBox(sphere);
return;
}
axisList.add(entity.getBox());
}


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);
TexturedModel model = new TexturedModel(Loader.loadToVao(verticesArray, texturesArray, normalsArray, indicesArray),min,max,vertices,furthest, textureLoc);
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) {
xArray.add(vertex.getPosition().x);
yArray.add(vertex.getPosition().y);
zArray.add(vertex.getPosition().z);
}
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) {
xArray.add(vertex.getPosition().x);
yArray.add(vertex.getPosition().y);
zArray.add(vertex.getPosition().z);
}
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) {
radius = Vector3f.dot(max, min);
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. – DMGregory Apr 3 '18 at 19:19
• @DMGregory I edited my post to include a description of the problem. – Macky ben Jonah Apr 4 '18 at 8:36

## 1 Answer

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;
float    radius         = magnatude(radiusVector);


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;
float    radius         = magnatude(radiusVector);
// add radius to the min to find the worldspace
// midpoint between min and max
Vector3f center_worldspace = min + radiusVector;


I'm not very fluent in java so I'm sticking to psuedocode for now. I hope the meaning still comes across, and this is even a little bit helpful. Let me know if you have any further questions. Good luck!

• I edited my post to include a description of the problem – Macky ben Jonah Apr 4 '18 at 8:36
• @MackybenJonah Added an edit section in response. – DrewAtWork Apr 4 '18 at 15:34
• no the center and pos is the same thing. – Macky ben Jonah Apr 4 '18 at 17:41
• Small problem here I can only add, subtract and dot in java Vector3f Off-topic here I know but is there any java libraries for OpenGL vector operations. – Macky ben Jonah Apr 4 '18 at 17:44
• @MackybenJonah If you could point me to some documentation on the java Vector3f your using I may be able to help you find a function that works. 'Magnitude' may be called 'Length'. And if there is some sort of 'Scale' function you can scale the vector by 0.5 which is equal to divide by 2. As far as a java OGL library I'm sorry but I don't have any recommendations. – DrewAtWork Apr 4 '18 at 20:29