# 3D AxisAngle spins at 180 degrees

I am using the Axis Angle technique on objects so that they are oriented away from a planet surface like in Super Mario Galaxy. I am using two unit vectors [0,1,0] and a normal vector from the planet. Is there any way to stop the 3D objects from spinning when they go near 180 degrees and still orient correctly? Could I use gimbal lock to fix it? This is what I have so far for my game in JavaScript:

// Angles are in radians
angle = Math.acos(dotProductVec3([0,1,0], forceNormal));

// Using two unit vectors to find the axis and angle
if (angle > Math.PI / 2)
{
forceRotation.angle = Math.acos(dotProductVec3([0,-1,0], forceNormal));
forceRotation.axis = vec3Normalize(vec3CrossProduct([0,-1,0], forceNormal));
}
else
{
forceRotation.angle = Math.acos(dotProductVec3([0,1,0], forceNormal));
forceRotation.axis = vec3Normalize(vec3CrossProduct([0,1,0], forceNormal));
}


Above I am using a negative number on the y axis to stop the spin when 180 degrees has been reached. The problem is the objects point towards the planet rather than away. Also I have tried Quaternions but they seem to spin too.

Update:

I watched a video on the hairy ball theory and restarted my work on this project. I managed to get the spaceship to align with the velocity like you said but the ship's linear y axis does not always point away from the middle of the planet. Is there a way to get the ship to have a properly aligned orbit like Super Mario Galaxy? Note I am using [0,0,1] since I can't figure out how to orient the ship any other way. The new code is below:

    let angle = Math.acos(dotProductVec3(vec3Normalize([0,0,1]), vec3Normalize(velocity)));
let axis = vec3Normalize(vec3CrossProduct([0,0,1], velocity));
newAngle = vec3EulerFromAxisAngle(axis, angle);


Any help would be much appreciated.

• Looks like you're trying to form a basis from an input direction. The Hairy Ball Theorem tells us there's no continuous, single-valued way to do this in 3D. So you'll need an additional input, like the object's "forward" direction from the previous frame. Then you can use that to choose a coordinate basis that's close to the previous one, avoiding sudden spins/lurches when you cross critical points. Aug 27, 2021 at 2:17
• @DMGregory I ended up using your previous frame idea when the spaceship is flying and when the spaceship hits something I used [0,1,0] to keep it stable. Aug 28, 2021 at 19:25
• If that solved your problem, want to post your solution as an answer below? Aug 28, 2021 at 20:05
• @DMGregory I have restarted my work on this question. My code below has been updated. If you have any ideas on how to fix the problem I would appreciate it. Sep 9, 2021 at 17:45

Below is some of the code I could get working. First I test for a collision with an object on the planet and use the [0,1,0] code. If there is no collision then I find the previous normal and use it to rotate. The forceAngle boolean orients the spaceship after it has had a collision and is only touching the planet. I am not sure why but I can't get the forceAngle to stop running.

if (collision > 1 || forceAngle === true)
{
forceRotation.angle = Math.acos(dotProductVec3([0,1,0], forceNormal));
forceRotation.axis = vec3Normalize(vec3CrossProduct([0,1,0], forceNormal));
if (collision === 1)
{
forceAngle = false;
}
}
else
{
forceAngle = true;

forceRotation.angle = Math.acos(dotProductVec3(oldForceNormal, forceNormal));
forceRotation.axis = vec3Normalize(vec3CrossProduct(oldForceNormal, forceNormal));
}