5

Background

I'm trying to create a FPS game in Java using LWJGL 3.0. I've set up a camera class, that has a pitch and yaw (roll is not being used). The camera itself extends Entity, as it has a model. This model I would like to appear to always be "in front" of the camera, wherever the camera is pointing. Each Entity has a method getTransformationMatrix() which returns a Matrix4f, that is then passed into the entity shader.

Problem

The model needs to point in the direction of the camera, as well as rotate around the camera, such that it is always in front. The object in this situation is hands with a gun, as shown in the photo below.

enter image description here

My Attempt

I am aware of basic trigonometry, so I got the object to rotate correctly for pitch and yaw, separately. This is my current implementation:

Yaw

@Override
public Matrix4f getTransformationMatrix() {
    modelX = getPosition().x + (radius * (float)Math.sin(Math.toRadians(getYaw())));
    modelZ = getPosition().z + (radius * (float)Math.cos(Math.toRadians(getYaw())));

    return Transform.createTransformationMatrix(new Vector3f(modelX, getPosition().y - 5, modelZ), new Vector3f(0, getYaw(), 0), getScale());
}

Pitch

@Override
public Matrix4f getTransformationMatrix() {
    modelZ = getPosition().z + (radius * (float)Math.sin(Math.toRadians(getPitch())));
    modelY = (getPosition().y - 5) + (radius * (float)Math.cos(Math.toRadians(getPitch())));

    return Transform.createTransformationMatrix(new Vector3f(getPosition().x, modelY, modelZ), new Vector3f(getPitch(), 0, 0), getScale());
}

I have done some research but I fear I have been stuck on this too long and need some fresh eyes. When I try to combine these 2 calculations, the model seems to move in the shape of a graph when looking at any yaw angle other than 0. Below is my attempt of combining these:

@Override
public Matrix4f getTransformationMatrix() {
    float zAxis = (radius * (float)Math.sin(Math.toRadians(getPitch())));
    modelY = (getPosition().y - 5) + (radius * (float)Math.cos(Math.toRadians(getPitch())));
    modelZ = getPosition().z + (zAxis * (float)Math.cos(Math.toRadians(getYaw())));
    modelX = getPosition().x + (radius * (float)Math.sin(Math.toRadians(getYaw())));

    return Transform.createTransformationMatrix(new Vector3f(modelX, modelY, modelZ), new Vector3f(getPitch(), getYaw(), 0), getScale());
}

The Transform.createTransformationMatrix() looks like the following:

public static Matrix4f createTransformationMatrix(Vector3f translation, Vector3f rotation, Vector3f scale) {
    transform3d = new Matrix4f();
    transform3d.setIdentity();
    Matrix4f.translate(translation, transform3d, transform3d);
    Matrix4f.rotate((float) Math.toRadians(rotation.x), new Vector3f(1, 0, 0), transform3d, transform3d);
    Matrix4f.rotate((float) Math.toRadians(rotation.y), new Vector3f(0, 1, 0), transform3d, transform3d);
    Matrix4f.rotate((float) Math.toRadians(rotation.z), new Vector3f(0, 0, 1), transform3d, transform3d);
    Matrix4f.scale(scale, transform3d, transform3d);
    return transform3d;
}

Thoughts

A friend suggested creating a unit vector that points in the direction of up, (ie. new Vector3f(0, 1, 0)) rotating the Vector by the pitch and yaw, then multiplying the Vector by the radius and adding it to the camera's position. I tried this, but I don't know how to rotate a Vector by an angle, and there seems to be no Vector3f.rotate() method in the slick-utils Vector3f class. Any help is is thoroughly appreciated as this has been giving me a headache for the past few days. Thanks!

7
  • 3
    Rotating for any angle around any axis is a matrix 4x4 as described here. Rotating for yaw and then for pitch is a matrix multiplication of two matrices. Be aware of the order, it isn't the same as pitch and then yaw.
    – Ripi2
    Commented Dec 5, 2018 at 16:42
  • Do you really need this matrix (i.e. placement in the world)? Wouldn't it be enough to have the matrix in view space, where it is constant? When you draw the model, set the view matrix to identity (since your model is already in view space) and set the model matrix appropriately (probably just a slight translation away from the camera). Commented Dec 6, 2018 at 22:04
  • @NicoSchertler I have a view matrix that is passed into the shader, and the transformation matrix which is also passed in, then they are multiplied in the shader to calculate world space for each vertex of the model. How would what you are suggesting apply to this? I tried applying just the view matrix in the shader but it didn't work properly
    – Kris Rice
    Commented Dec 8, 2018 at 12:55
  • 1
    I was saying not to apply the view matrix (set to identity). Then, the model will have a constant location with respect to the camera. Commented Dec 8, 2018 at 15:41
  • I will check when I'm home and let you know, thanks!
    – Kris Rice
    Commented Dec 8, 2018 at 22:25

1 Answer 1

3
+50

What we normally do is, yes, take a unit-length vector and use it as our "axis". In 2D rotation we use an axis - the Z axis - all the time.

3D rotation

If you were to look at the axis, like in 2D, you would see something like this

2D rotation

So, to rotate a point in 3D you can use a matrix or a vector. I recommend the vector first so you can get an idea of how 3D rotation works. It blows your mind!

I'll drop the code from a Vector3f class from theBennyBox . If you're interested in more of this math check out theBennyBox on Youtube.

Vector3f

    public Vector3F rotate(float angle, Vector3F axis) {
    double a = Math.toRadians(angle / 2f);
    float hs = (float) Math.sin(a);
    float hc = (float) Math.cos(a);
    Vector4F r = new Vector4F(axis.getX() * hs, axis.getY() * hs, axis.getZ() * hs, hc);
    Vector4F rc = r.conjugate();
    r = r.multiplyAsQuat(this).multiplyAsQuat(rc);

    return new Vector3F(r.getX(), r.getY(), r.getZ());
    }

Vector 4f

    public Vector4F multiplyAsQuat(Vector3F v) {
    float o = -x * v.getX() - y * v.getY() - z * v.getZ();
    float a = w * v.getX() + y * v.getZ() - z * v.getY();
    float b = w * v.getY() + z * v.getX() - x * v.getZ();
    float c = w * v.getZ() + x * v.getY() - y * v.getX();

    return new Vector4F(a, b, c, o);
}

    public Vector4F conjugate() {
    return new Vector4F(-x, -y, -z, w);
}

    public Vector4F multiplyAsQuat(Vector4F qt) {

    float o = w * qt.getW() - x * qt.getX() - y * qt.getY() - z * qt.getZ();
    float a = x * qt.getW() + w * qt.getX() + y * qt.getZ() - z * qt.getY();
    float b = y * qt.getW() + w * qt.getY() + z * qt.getX() - x * qt.getZ();
    float c = z * qt.getW() + w * qt.getZ() + x * qt.getY() - y * qt.getX();

    return new Vector4F(a, b, c, o);
}

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