# Swept AABB: finding collision normal in 3D?

I was following this tutorial which explains how to detect and resolve a 2d swept aabb collisions. I got the detection part working on 3d, but know I need a way to properly make a response, like sliding on the face because right now my player just get stuck on the wall. To do that, I need the surface normal, but I couldn't correctly translate the normal calculation in the tutorial from 2d to 3d. Here is the code, written in Java, but you can always check the tutorial:

private float sweptAABB(Vector3f velocity, float x, float y, float z, float minTime){
// find the distance between the objects on the near and far sides for both x, y and z
// and find time of collision and time of leaving for each axis
if (velocity.x > 0.0f) {
invEntry.x = x - maxI.x;
entry.x = invEntry.x / velocity.x;
invExit.x = x + 1 - minI.x;
exit.x = invExit.x / velocity.x;
} else if (velocity.x < 0.0f){
invEntry.x = x + 1 - minI.x;
entry.x = invEntry.x / velocity.x;
invExit.x = x - maxI.x;
exit.x = invExit.x / velocity.x;
} else {
invEntry.x = x + 1 - minI.x;
invExit.x = x - maxI.x;
entry.x = -Float.MAX_VALUE;
exit.x = Float.MAX_VALUE;
}

if (velocity.y > 0.0f) {
invEntry.y = y - maxI.y;
entry.y = invEntry.y / velocity.y;
invExit.y = y + 1 - minI.y;
exit.y = invExit.y / velocity.y;
} else if (velocity.y < 0.0f){
invEntry.y = y + 1 - minI.y;
entry.y = invEntry.y / velocity.y;
invExit.y = y - maxI.y;
exit.y = invExit.y / velocity.y;
} else {
invEntry.y = y + 1 - minI.y;
invExit.y = y - maxI.y;
entry.y = -Float.MAX_VALUE;
exit.y = Float.MAX_VALUE;
}

if (velocity.z > 0.0f) {
invEntry.z = z - maxI.z;
entry.z = invEntry.z / velocity.z;
invExit.z = z + 1 - minI.z;
exit.z = invExit.z / velocity.z;
} else if (velocity.z < 0.0f){
invEntry.z = z + 1 - minI.z;
entry.z = invEntry.z / velocity.z;
invExit.z = z - maxI.z;
exit.z = invExit.z / velocity.z;
} else {
invEntry.z = z + 1 - minI.z;
invExit.z = z - maxI.z;
entry.z = -Float.MAX_VALUE;
exit.z = Float.MAX_VALUE;
}
// find the earliest/latest times of collision
if (entry.y > 1.0f) entry.y = -Float.MAX_VALUE;
if (entry.x > 1.0f) entry.x = -Float.MAX_VALUE;
if (entry.z > 1.0f) entry.z = -Float.MAX_VALUE;

float entryTime = Math.max(Math.max(entry.x,entry.z),entry.y);

if(entryTime >= minTime) return 1.0f;

float exitTime = Math.min(Math.min(exit.x,exit.z),exit.y);

if(entry.x < 0.0f && entry.y < 0.0f && entry.z < 0.0f) // equals to entryTime < 0.0f
return 1.0f;

if (entryTime > exitTime) return 1.0f;

if(entry.x > 1.0f) {
if(max.x < x || min.x > x + 1)
return 1.0f;
}

if(entry.y > 1.0f){
if(max.y < y || min.y > y + 1)
return 1.0f;
}

if(entry.z > 1.0f){
if(max.z < z || min.z > z + 1)
return 1.0f;
}
return entryTime;


Basically, in the tutorial, to get normals, they do a test on the entry values, here is how I tried to translate this to 3d:

if (entry.x > entry.y) {
if(entry.x > entry.z){
if (invEntry.x < 0.0f)
normal.x = 1.0f;
else
normal.x = -1.0f;

normal.y = 0.0f;
normal.z = 0.0f;
}else{
if (invEntry.z < 0.0f)
normal.z = 1.0f;
else
normal.z = -1.0f;

normal.x = 0.0f;
normal.y = 0.0f;
}
} else {
if (entry.y > entry.z) {
if (invEntry.y < 0.0f)
normal.y = 1.0f;
else
normal.y = -1.0f;

normal.x = 0.0f;
normal.z = 0.0f;

} else {
if (invEntry.z < 0.0f)
normal.z = 1.0f;
else
normal.z = -1.0f;

normal.x = 0.0f;
normal.y = 0.0f;
}
}


I'm getting correct directions, but the values are always negative and I have trouble picturing the maths behind the normal testing. My question is: how can I correctly calculate the normals based on the code from the tutorial?

Thanks!

• The sign of the normal should always be opposite the sign of your velocity on that component, so you can use that as a shortcut. – DMGregory Jan 26 at 14:13
• Wow what a simple and yet elegant solution, this solves the issue and simplifies the code thanks! You should post an answer so I can accept it as the correct one – Thibault Abry Jan 26 at 15:28

Based on what @DMGregory proposed in the comments, here is the simplified and working code to calculate the normals:

if (entry.x > entry.y) {
if(entry.x > entry.z){
normal.x = -Maths.sign(velocity.x);
normal.y = 0.0f;
normal.z = 0.0f;
}else{
normal.z = -Maths.sign(velocity.z);
normal.x = 0.0f;
normal.y = 0.0f;
}
} else {
if (entry.y > entry.z) {
normal.y = -Maths.sign(velocity.y);
normal.x = 0.0f;
normal.z = 0.0f;

} else {
normal.z = -Maths.sign(velocity.z);
normal.x = 0.0f;
normal.y = 0.0f;
}
}


With Maths.sign(int a){ return a >= 0 ? 1 : -1; }