0
\$\begingroup\$

I'm implementing a space fps game (there is no up or down in space!) using LWJGL3 and JOML (math library).

Entities in my game have a rotation based on the Forward, Up and Right vectors. I use pitch, yaw and roll methods to rotate my entities. The methods can be seen in the pseudo code further down.

When I construct the model matrix for an entity I calculate a rotation matrix based on the 3 vectors described above and apply it. It can be seen in the pseudo code further down.

I also use angular and linear acceleration and velocity to rotate and move an entity. It can be seen in the pseudo code further down.

Entity:

Vector3f position
Vector3f scale
Vector3f forward
Vector3f up
Vector3f right
private Vector3f linearAcc
private Vector3f linearVel
private Vector3f angularAcc
private Vector3f angularVel

public void pitch(double angle) {
    forward.mul((float) Math.cos(angle), tempVector1).add(up.mul((float) Math.sin(angle), tempVector2)).normalize(forward);
    right.cross(forward, up);
}

public void roll(double angle) {
    right.mul((float) Math.cos(angle), tempVector1).add(up.mul((float) Math.sin(angle), tempVector2)).normalize(right);
    right.cross(forward, up);
}

public void yaw(double angle) {
    right.mul((float) Math.cos(angle), tempVector1).add(forward.mul((float) Math.sin(angle), tempVector2), right);
    up.cross(right, forward);
}

public Matrix4f getRotationMatrix(Matrix4f dest) {
    dest.set(
        right.x, right.y, right.z, 0,
        forward.x, forward.y, forward.z, 0,
        up.x, up.y, up.z, 0,
        0, 0, 0, 1);
    return dest;
}

Model matrix:

public static Matrix4f createModelMatrix(Vector3f position, Matrix4f rotationMatrix, Vector3f scale) {
    Matrix4f modelMatrix = new Matrix4f();
    modelMatrix.translate(position);
    modelMatrix.mul(rotationMatrix);
    modelMatrix.scale(scale);
    return modelMatrix;
}

Basic logic for rotating and moving an entity:

        // Angular acceleration:
        // Angular acceleration is reset every update
        entity.getAngularAcc().zero();

        // Components may rotate an entity by applying angular acceleration
        Vector3f someAngularThrust = new Vector3f(0, 0, 0.025f);
        entity.getAngularAcc().add(someAngularThrust );

        // The accumulated angular acceleration is added to the angular velocity
        entity.getAngularVel().fma(dt, movement.getAngularAcc());

        // Finally, the entity is rotated by the angular velocity
        entity.pitch(dt * entity.getAngularVel().x);
        entity.yaw(dt * entity.getAngularVel().y);
        entity.roll(dt * entity.getAngularVel().z);

        // The basic logic applies for linear movement:
        // Linear acceleration is reset every update
        entity.getLinearAcc().zero();

        // Components may move an entity by applying linear acceleration
        Vector3f someLinearThrust = entity.getForward().mul(5);
        entity.getLinearAcc().add(someLinearThrust );

        // The accumulated linear acceleration is added to the linear velocity
        entity.getLinearVel().fma(dt, entity.getLinearAcc());

        // Finally, the entity is moved by the linear velocity
        entity.getPosition().fma(dt, entity.getLinearVel());

So far it seems that this setup works, but it does not give me all the functionality I need in my game:

  • I want an alternative way of setting/modifying the rotation of an entity - right now it can only be done by setting the Forward, Up and Right vectors or using the pitch, yaw and roll methods.

  • I want to be able to represent the rotation of an entity with a smaller unit (a rotation vector / axis?). It is a multiplayer game where I need to send the orientation of entities. Sending 2 or more 3d vectors to represent the orientation is not optimal.

From what I understand quaternions can do all that I want. I tried to use it in my game (Quaternionf in JOML), but couldn't get it to work properly:

  • I couldn't get the pitch, yaw and roll methods to work.
  • The model matrix didn't work. The facing direction of the entities did not match the direction they were moving - they were supposed to move in the direction they were facing.
  • The movement logic didn't seem to work either (even when not using pitch, yaw and roll). Could have got something to do with the above point.

Any ideas how I should go on about to use quaternions in my code?

\$\endgroup\$
1
\$\begingroup\$

Sorry you've been waiting so long for an answer. Learning quaternions was a huge headache for me, but it was definitely worth it. Here is my code. I don't fully understand it some times, but the more I play with it the better I get.

Just play around with it and you'll see what's going on.

import org.joml.Matrix4f;
import org.joml.Quaternionf;
import org.joml.Vector3f;



Quaternionf dest1 = new Quaternionf();
Quaternionf dest2 = new Quaternionf();
Vector3f v = new Vector3f();


// verticesModel is the initial position of my vertices 
//before the 3D transformations are done
// Vector3f[] verticesModel

for(int i = 0 ; i < verticesModel.length;i++)
{

        // I have a global variable 'v' just to avoid making a new Vector3f object 60 times every second when the draw() method is run
        v.x = verticesModel[i].x;
        v.y = verticesModel[i].y;
        v.z = verticesModel[i].z;


        // blank out my quaternions
        dest1.w = 1f;
        dest1.x = 0f;
        dest1.y = 0f;
        dest1.z = 0f;

        dest2.w = 1f;
        dest2.x = 0f;
        dest2.y = 0f;
        dest2.z = 0f;

        // I keep my pitch/yaw/roll in another Vector3F called rotationXYZ as degrees
        float x, y, z;
        x = rotationXYZ.x;// + rotationXYZ.x;// testX;
        y = rotationXYZ.y;// + rotationXYZ.y;//testY;
        z = rotationXYZ.z;// + rotationXYZ.z;//testZ;

        // just make sure the degrees values don't get bigger than they need to be
        x = x % 360.0f;
        y = y % 360.0f;
        z = z % 360.0f;

        // convert to radians and start transforming
        dest2.rotate((float) Math.toRadians(x), (float) Math.toRadians(y), (float) Math.toRadians(z), dest1);

        dest1.transform(v);

        // vv is the new value for v after the transformations
        // Vector3f[] vertices is the array of vertices I use to draw
        Vector3f vv = vertices[i];

        vv.x = v.x;
        vv.y = v.y;
        vv.z = v.z;

}

\$\endgroup\$
  • \$\begingroup\$ Wauwzor, thanks for your answer! :D I did actually solve my problem some time afterwards (a month or so) when taking a look at it again. I don't know exactly how a quaternion work mathematically, but I understand how to use it at least. It's super-sweet, not only because you can do a lot with it, but also because it requires less data. It certainly did make my multiplayer implementation a whole lot easier and data-packets more compact. \$\endgroup\$ – Marcus Persson Mar 16 '18 at 13:58
  • \$\begingroup\$ Congrats man! Let us know how the game turns out. \$\endgroup\$ – Joseph Doss Mar 17 '18 at 10:13

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.