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 Answer 1

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\$
2
  • \$\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\$ Mar 16, 2018 at 13:58
  • \$\begingroup\$ Congrats man! Let us know how the game turns out. \$\endgroup\$ Mar 17, 2018 at 10:13

You must log in to answer this question.

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