0
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ 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. \$\endgroup\$
    – DMGregory
    Aug 27, 2021 at 2:17
  • \$\begingroup\$ @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. \$\endgroup\$
    – Jon White
    Aug 28, 2021 at 19:25
  • \$\begingroup\$ If that solved your problem, want to post your solution as an answer below? \$\endgroup\$
    – DMGregory
    Aug 28, 2021 at 20:05
  • \$\begingroup\$ @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. \$\endgroup\$
    – Jon White
    Sep 9, 2021 at 17:45

1 Answer 1

0
\$\begingroup\$

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));
}
\$\endgroup\$
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .