2
\$\begingroup\$

How do I create 3D velocity with a 3D angle and 1D thrust like the 2D algorithm below?

angle.z += angularVelocity.z * timeStep;

velocity.x = thrust * cosf(angle.z);
velocity.y = thrust * sinf(angle.z);

position.x += velocity.x * timeStep;
position.y += velocity.y * timeStep;

I want to simplify my 3D OpenGL motion by having the final angle and position correct no matter where the spaceship's thrust is.

My spaceship uses the OpenGL motion functions below:

glPushMatrix();
    glTranslatef(position.x, position.y, position.z);
    glRotatef(rad2deg(angle.x), 1.0, 0.0, 0.0);
    glRotatef(rad2deg(angle.y), 0.0, 1.0, 0.0);
    glRotatef(rad2deg(angle.z), 0.0, 0.0, 1.0);
    glBegin(GL_QUADS);
        // CCW Quad
    glVertex3f(-1.0, 1.0, 0.0);
    glVertex3f(-1.0,-1.0, 0.0);
    glVertex3f( 1.0,-1.0, 0.0);
    glVertex3f( 1.0, 1.0, 0.0);

    glEnd();
glPopMatrix();

The problem is that the 3D spaceship rotates but the thrust always goes off toward the positive x axis. The angles are Euler in degrees and radians.

\$\endgroup\$
  • \$\begingroup\$ There are a lot more ways to represent a direction/angle in 3D than there are in 2D, so you'll need to pick one first in order for this question to be well-specified. Does your game use (some specific variant of) Euler/Tait-Bryan angles, quaternions, rotation matrices, direction vectors, etc...? \$\endgroup\$ – DMGregory Jul 24 '17 at 20:05
  • \$\begingroup\$ @DMGregory I just updated my question with the OpenGL shape and motion. \$\endgroup\$ – Jon White Jul 25 '17 at 0:38
2
\$\begingroup\$

Trigonometry

As stated in this article, 3D rotations (rX, rY, rZ; for their respective axes) transform a point P (xi,yi,zi) into another point P' (xf, yf, zf) in the following way. Note: the rotations can be ordered in any way, but I'm going to do XYZ.

X-Axis rotation

yf = yi*cos(rotX) - zi*sin(rotX)
zf = yi*sin(rotX) + zi*cos(rotX)
xf = xi

Y-Axis rotation

zf = zi*cos(rotY) - xi*sin(rotY)
xf = zi*sin(rotY) + xi*cos(rotY)
yf = yi

Z-Axis rotation

xf = xi*cos(rotZ) - yi*sin(rotZ)
yf = xi*sin(rotZ) + yi*cos(rotZ)
zf = zi

Application

With this knowledge, we can construct a unit vector that represents the direction that your spaceship is moving in. Let's start by having it point forward, since opengl's view matrix point's forward (-Z) by default.

dir_init = vec3( 0, 0, -1 )
dir = vec3()

Now, we can plug dir_init.x in for xi and dir.x in for xf, and the same for the other two dimensions, and do the math above. After that, all we have to do is add each component to its respective component in the spaceships coordinates.

spaceship.x += dir.x * speed
spaceship.y += dir.y * speed
spaceship.z += dir.z * speed

Disclaimer: I started working with this concept today, and if I'm missing anything, feel free to comment.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Are the X and Y axes supposed to be rotX and rotY instead of rotZ? Also how do you add all three rotation axes together? I seem to be getting acceleration when I add them. \$\endgroup\$ – Jon White Jul 29 '17 at 22:00
  • \$\begingroup\$ Ah! I'll fix that!! \$\endgroup\$ – clabe45 Jul 29 '17 at 22:34
  • \$\begingroup\$ By 'add'ing the three rotation axes together, do you mean applying all three rotations to the direction of the ship? You apply all three by simply using/updating the same variables with each rotation. \$\endgroup\$ – clabe45 Jul 29 '17 at 22:36
0
\$\begingroup\$

Looking again at the code again gave me a great idea! Since velocity is just the change of position use OpenGL to do the grunt work. I was able to get the code below working, although, the answer above is better optimized. The code below is updated inside the drawing function and thrustSpeed and angularVelocity are changed in the keyboard function.

angle.x += angularVelocity.x * timeStep;
angle.y += angularVelocity.y * timeStep;
angle.z += angularVelocity.z * timeStep;
float matrix[16];

glPushMatrix();
    glLoadIdentity();

    glRotatef(degreesFromRadians(angle.z), 0.0, 0.0, 1.0);
    glRotatef(degreesFromRadians(angle.y), 0.0, 1.0, 0.0);
    glRotatef(degreesFromRadians(angle.x), 1.0, 0.0, 0.0);
    glTranslatef( 0.0, thrustSpeed, 0.0);

    glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
glPopMatrix();

position.x += matrix[12] * timeStep;
position.y += matrix[13] * timeStep;
position.z += matrix[14] * timeStep;

glPushMatrix();
    glTranslatef( position.x, position.y, position.z);
    glRotatef(degreesFromRadians(angle.z), 0.0, 0.0, 1.0);
    glRotatef(degreesFromRadians(angle.y), 0.0, 1.0, 0.0);
    glRotatef(degreesFromRadians(angle.x), 1.0, 0.0, 0.0);

    glBegin(GL_QUADS);
    // CCW Quad
    glColor3f(1.0,1.0,1.0);

    glVertex3f(-1.0, 1.0, 0.0);
    glVertex3f(-1.0,-1.0, 0.0);
    glVertex3f( 1.0,-1.0, 0.0);
    glVertex3f( 1.0, 1.0, 0.0);

    glEnd();
glPopMatrix();

I’m just rotating around the OpenGL origin and then adding the matrix’s position as velocity.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.