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.
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!
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With