Hot questions for Using Lightweight Java Game Library in rotation

Question:

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!


Answer:

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);
}

Question:

Long story short, I'm making an LWJGL engine, and am drawing a basic QUAD. When I draw this QUAD and use the Keyboard listener, it moves perfectly, up/down/left/right.

When I translate and rotate however, it also pivots around its center point just fine, use both of them together, and then you have a problem. Rotation starts to go off axis, and everytime you move, you pivot around a strange point somewhere else.

How can I make it so I can move the QUAD (not relative to rotation), aswell as rotate it?

Edit 1

I have found out that a major problem with this QUAD, is that when I goto rotate it, my entire screen, (text included) rotates...

My current results:

Before Movement

After Movement (Hopefully you notice that it moved in a circle, instead of just LEFT when I move LEFT.)

Code:

(Display Setup)

try {
    Display.setDisplayMode(new DisplayMode(width, height));
    Display.setVSyncEnabled(vsync);
    Display.create();
    open = true;

    GL11.glMatrixMode(GL11.GL_PROJECTION);
    GL11.glLoadIdentity();
    // Sets (0, 0) to the top left corner.
    GL11.glOrtho(0, this.width, this.height, 0, 1, -1);
    GL11.glMatrixMode(GL11.GL_MODELVIEW);
} catch (LWJGLException e) {
    e.printStackTrace();
}

(Keyboard listener)

public void userLogic() {
    if (keyboard.isKeyDown(Keyboard.KEY_A)) {
        rect.setLocation(rect.x - 1, rect.y, rect.width, rect.height);
        rect.setRotation(-1);
    } else if (keyboard.isKeyDown(Keyboard.KEY_D)) {
        rect.setLocation(rect.x + 1, rect.y, rect.width, rect.height);
        rect.setRotation(1);
    } if (keyboard.isKeyDown(Keyboard.KEY_W)) {
        rect.setLocation(rect.x, rect.y - 1, rect.width, rect.height);
    } else if (keyboard.isKeyDown(Keyboard.KEY_S)) {
        rect.setLocation(rect.x, rect.y + 1, rect.width, rect.height);
    }
}

(Movement/Rotation Logic)

public void setRotation(float degrees) {
    // TODO: Fix whatever the hell the problem is here.
    GL11.glTranslatef(x + (width / 2), y/* + (height / 2)*/, 0);
    GL11.glRotatef(degrees, 0f, 0f, 1f);
    GL11.glTranslatef(-(x + (width / 2)), -(y/* + (height / 2)*/), 0);
}

public void setLocation(int x, int y, int width, int height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
}

(Drawing the QUAD, (detached from rest of the rendering process))

public void render() {
    switch(type) {
    case IMAGE:
        // Not ready yet.
        break;
    case TRIANGLE:
        // Not ready yet.
        break;
    case RECTANGLE:
        GL11.glColor3f(colour.red, colour.green, colour.blue);

        // X, Y, WIDTH, HEIGHT are the QUADS coords, not the displays or anything else's.

        GL11.glBegin(GL11.GL_QUADS);
            GL11.glVertex2f(x, y);
            GL11.glVertex2f(x + width, y);
            GL11.glVertex2f(x + width, y + height);
            GL11.glVertex2f(x, y + height);
        GL11.glEnd();
        break;
    }
}

Any help will be gratefully appreciated, if you are going to downvote, please give a reason so I can improve this question, or even better, comment instead.


Answer:

The problem here is the order in which you call the translate and rotate functions of OpenGL in your setRotationfunction . The order of calling must be Translation(if any)-> Rotation(if any)->Scaling. You cannot change this order of calling or mix them up in anyway. Or else you'll not get the desired results. This is because the way in which these commands are processed internally by OpenGL.

Change your SetRotation function to

public void setRotation(float degrees) {
    // TODO: Fix whatever the hell the problem is here.
    GL11.glMatrixMode(GL11.GL_MODELVIEW);
    GL11.glLoadIdentity();
    GL11.glTranslatef(x , y , 0);       
    GL11.glRotatef(degrees, 0f, 0f, 1f);

}

You'll notice that if you add the lines

GL11.glTranslatef(x , y, 0);

underneath

GL11.glRotatef(degrees, 0f, 0f, 1f);

, the result will be completely different. Meaning the order is important.

There are some problems in the userLogic()function as well. In this case, you need to create a global variable to store rotation and then pass it to setRotation()

float rotValue = 0;
public void userLogic() {
    if (keyboard.isKeyDown(Keyboard.KEY_A)) {
        rect.setLocation(rect.x - 1, rect.y, rect.width, rect.height);
        rotValue-=.1f;
        rotValue=rotValue<0?rotValue+360:rotValue;
        rect.setRotation(rotValue);
    } else if (keyboard.isKeyDown(Keyboard.KEY_D)) {
        rect.setLocation(rect.x + 1, rect.y, rect.width, rect.height);
        rotValue+=.1f;
        rotValue=rotValue>=360?rotValue-360:rotValue;
        rect.setRotation(rotValue);
    } if (keyboard.isKeyDown(Keyboard.KEY_W)) {
        rect.setLocation(rect.x, rect.y - 1, rect.width, rect.height);
    } else if (keyboard.isKeyDown(Keyboard.KEY_S)) {
        rect.setLocation(rect.x, rect.y + 1, rect.width, rect.height);
    }
}

Finally inside the render() function, pass in vertices centered around (0,0). glTranslatef will take care of translation.

case RECTANGLE:
        GL11.glColor3f(colour.red, colour.green, colour.blue);
        GL11.glBegin(GL11.GL_QUADS);
        GL11.glVertex2f(-width/2, -height/2);
        GL11.glVertex2f(width/2, -height/2);
        GL11.glVertex2f(width/2, height/2);
        GL11.glVertex2f(-width/2, height/2);
        GL11.glEnd();
        GL11.glMatrixMode(GL11.GL_MODELVIEW); //resetting the rotation
        GL11.glLoadIdentity();  //and translation so that other
                                //objects are unaffected
            break;

Here is a simple lwjgl project I made showcasing translation and rotation of a quad and triangle https://gist.github.com/Coditivity/0eedc86447a509f6b5ef

Question:

I am trying to have 2 cubes rotate differently.

to set the rotation, I do this.

GL11.glRotatef(rot[0], 1.0f, 0.0f, 0.0f);
GL11.glRotatef(rot[1], 0.0f, 1.0f, 0.0f);
GL11.glRotatef(rot[2], 0.0f, 0.0f, 1.0f);

Instead of using GL11.glLoadIdentity() to reset the rotation, inside the cube class, I do something like this.

GL11.glRotatef(rot[0] * -1.0f, 1.0f, 0.0f, 0.0f);
GL11.glRotatef(rot[1] * -1.0f, 0.0f, 1.0f, 0.0f);
GL11.glRotatef(rot[2] * -1.0f, 0.0f, 0.0f, 1.0f);

This should reset the rotation of each axis.

The array "rot" holds the x, y, and z rotations, and is updated through these 3 methods in the cube class.

public void pitch(float angle) {
    rot[0] = angle;
}

public void yaw(float angle) {
    rot[1] = angle;
}

public void roll (float angle) {
    rot[2] = angle;
}

Individually, each "GL11.glRotatef(etc,etc,etc,etc) and GL11.glRotatef(etc * -1.0f, etc,etc,etc)" works fine, but when they are all together, strange rotations happen.

I'm not sure if this is something to do with Gimbal Lock or my code, please help.

Thanks in advance.


Answer:

Your inversion code seems to be wrong. Note that matrix multiplications are not commutative:

The inverse of

R(x) * R(y) * R(z)

(which is simmilar to your three rotatef calls) is

R(-z) * R(-y) * R(-x)

. This means that you have to change the order in which you call your second glRotatef commands.

Question:

I'm currently following the OpenGL tutorials over at https://learnopengl.com/ except I'm doing it in Java instead of C++. It's going quite well but I've stumbled upon something that may be caused by a difference in implementation, unless I'm doing something wrong.

I'm at the part where a cube has just been rendered and is rotating. The tutorial gives the following code:

model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));```

To elaborate on this, model is the model matrix. The GLM library is used to rotate this matrix, using glfwGetTime() which returns the time in seconds since initialization. The part that I'm curious about is the vec3(0.5f, 1.0f, 0.0f). Here they specify which Euler angles/axes to rotate around, and by the looks of it, specifying 0.5f for the X axis rotates it half the amount on X compared to Y here. The resulting cube looks like this:

https://i.imgur.com/caK1BTS.mp4

Back to Java, I have set up a similar thing. I run the following on the model matrix:

Matrix4f.rotate(timeSinceStart * (float) Math.toRadians(50), new Vector3f(0.5f, 1.0f, 0.0f),  model, model);

The resulting cube rotates like this, oddly enough:

https://i.imgur.com/0ZqpgEA.mp4

If I change things up by separating which axe is affected per rotation, the cube looks normal like in this video:

https://i.imgur.com/u2zV2Zo.mp4

Code:

Matrix4f.rotate(timeSinceStart * (float) Math.toRadians(50), new Vector3f(0.0f, 1.0f, 0.0f),  model, model);
Matrix4f.rotate(timeSinceStart * (float) Math.toRadians(25), new Vector3f(1.0f, 0.0f, 0.0f),  model, model);

The difference here is that instead of doing "half" an axis, I instead do half the degrees on a full axis.

My question then is: Is it wrong of me to attempt to rotate around an axis that is not set to 1.0f? Is the GLM implementation of the 4x4 matrix rotate function different from the LWJGL-Utils' Matrix4f class?

PS: I'm using LWJGL 2 and Java 8u141. I'd be happy to provide more code about my setup if requested, but the entire process for rendering a cube is extensive. I've done the tutorial 2-3 times from scratch with the same result, so I think there's something going on here.


Answer:

According to this source in GLM GitHub repository GLM does rotation axis normalization before constructing transformation matrix. LWJGL-Utils Matrix4f class doesn't do this. So probably you should just normalize rotation axis (0.5f, 1.0f, 0.0f) by yourself.

Also, I'm not shure that you're right when calling rotation axis as 'Euler angles'. Euler angles may define rotation about first axis and then about derived second axis and so on. They basically define rotation as combination of three rotations about two or three base axes. Here you have one rotation axis and rotation angle.

See:

Question:

I have an object that can rotate on all axes, my problem is that if you rotate the object on the y axis (eg 90 °) the rotation on the z axis is incorrect because it rotates over the x axis,Instead, rotation on the x axis continues to function perfectly, If the y axis is set to 0 °, the rotation of the z axis returns to function correctly.

to rotate and translate object i use this code:

public static Matrix4f createTransformationMatrix(Vector3f translation , float rx, float ry, float rz , float scale){
    Matrix4f matrix = new Matrix4f();
    matrix.setIdentity();
    Matrix4f.translate(translation,matrix,matrix);
    Matrix4f.rotate((float) Math.toRadians(rx),new Vector3f(1, 0, 0) , matrix,matrix);
    Matrix4f.rotate((float) Math.toRadians(ry),new Vector3f(0, 1, 0) , matrix,matrix);
    Matrix4f.rotate((float) Math.toRadians(rz),new Vector3f(0, 0, 1) , matrix,matrix);
    Matrix4f.scale(new Vector3f(scale, scale, scale), matrix, matrix);
    return matrix;}

Answer:

That is an intrinsic singularity in Euler angle rotations, called Gimbal lock. Use quaternions for rotations instead (can specify an arbitrary axis).

Question:

I've been having a few problems with my fragment shader. After a bit of research, it appears that drivers clean up unused variables (ones which have no effect on the output?), which can cause glSetUniform and glGetUniform to return -1.

My current problem is that I'm attempting to rotate a texture 180 degrees, but it appears that I'm doing something incorrectly, as the uniform int "top" appears to be garbage collected, and cannot be found. The texture is not rotated at all, but still renders. The uniform "top" returns -1, which should not happen.

Here's my relevant code:

Rendering and Shader Enable code: (Shader.PIPE.enable() does call glUseProgram())

    Shader.PIPE.enable();
    Shader.PIPE.setUniformMat4f("vw_matrix", Matrix4f.translate(new Vector3f(xScroll * 0.03f, 0.0f, 0.0f)));    
    Pipe.getTexture().bind();
    Pipe.getMesh().bind();

    for (int i = 0; i < 5 * 2; i++) {
        Shader.PIPE.setUniformMat4f("ml_matrix", pipes[i].getModelMatrix());
        Shader.PIPE.setUniform1i("top",  i % 2 == 0 ? 1 : 0);
        Pipe.getMesh().draw();
    }
    Pipe.getMesh().unbind();
    Pipe.getTexture().unBind();

Pipe.frag:

#version 330 core

layout (location = 0) out vec4 color;

in DATA 
{
    vec2 tc;
} fs_in;

uniform sampler2D tex;
uniform int top;

void main() 
{   
    vec2 myTc = vec2(fs_in.tc.x, fs_in.tc.y);
    if (top == 1) {
        myTc.y = top - myTc.y;  
    }

    color = texture(tex, fs_in.tc);
    if (color.w  < 1.0)
        discard;
}

Pipe.vert:

#version 330 core

layout (location = 0) in vec4 position;
layout (location = 1) in vec2 tc;

uniform mat4 pr_matrix;
uniform mat4 vw_matrix = mat4(1.0);
uniform mat4 ml_matrix = mat4(1.0);

out DATA
{
    vec2 tc;
} vs_out;

void main() 
{
    gl_Position = pr_matrix * vw_matrix * ml_matrix * position;
    vs_out.tc = tc;
}

Answer:

The compiler can not only eliminate completely unused variables, but also values that do not contribute to the result. In your code, top is used here:

if (top == 1) {
    myTc.y = top - myTc.y;  
}

But myTc is not used later in the shader. So the value of myTc has no effect on the output of the shader. Which in turn means that the value of top has no effect. Or in other words, the result of the shader is the same, independent of the value of top.

This changes if myTc is used later in the shader, in a way that influences the result. I actually suspect that you meant to do that, and use it as the texture coordinates to sample your texture. So instead of:

color = texture(tex, fs_in.tc);

you should use:

color = texture(tex, myTc);

Now top will be used, and you should get the desired result.

Question:

I have been trying to make a 3D game engine. So far everything works except rotating bounding box. I have tried different ways to do it that I found here. Almost everything is the same. "Make rotation matrix and apply it to your bounding boxes min and max." That is the way I am doing it now, but there is all kinds of problem. I think it is rotating around the worlds zero point. Also I have two objects showing the min and max points and after rotating it is not colliding between these two things.

My transformation matrix:

public static Matrix4f createTransformationMatrix(Vector3f translation, float rx, float ry, float rz, float scale) {

    Matrix4f matrix = new Matrix4f();

    matrix.setIdentity();

    Matrix4f.translate(translation, matrix, matrix);

    Matrix4f.rotate((float) Math.toRadians(rx), new Vector3f(1,0,0), matrix, matrix);
    Matrix4f.rotate((float) Math.toRadians(ry), new Vector3f(0,1,0), matrix, matrix);
    Matrix4f.rotate((float) Math.toRadians(rz), new Vector3f(0,0,1), matrix, matrix);

    Matrix4f.scale(new Vector3f(scale, scale, scale), matrix, matrix);

    return matrix;

}

Updating collision box:

Matrix4f rotationMin = Maths.createTransformationMatrix(min, rotX, rotY, rotZ, scale);
Matrix4f rotationMax = Maths.createTransformationMatrix(max, rotX, rotY, rotZ, scale);

min = Maths.transform(min, rotationMin);
max = Maths.transform(max, rotationMax);

min.x += difX / 2;
min.y += difY / 2;
min.z += difZ / 2;

max.x += difX / 2;
max.y += difY / 2;
max.z += difZ / 2;

And applying matrix to min and max:

public static Vector3f transform(Vector3f vector, Matrix4f matrix) {

    Vector4f vec = new Vector4f(vector.x, vector.y, vector.z, 1.0f);

    Vector4f result = new Vector4f(

            (vec.x * matrix.m00) + (vec.y * matrix.m01) + (vec.z * matrix.m02) + (vec.w * matrix.m03),
            (vec.x * matrix.m10) + (vec.y * matrix.m11) + (vec.z * matrix.m12) + (vec.w * matrix.m13),
            (vec.x * matrix.m20) + (vec.y * matrix.m21) + (vec.z * matrix.m22) + (vec.w * matrix.m23),
            1

    );

    return new Vector3f(result.x / result.w, result.y / result.w, result.z / result.w);

}

All this is done with LWJGL and Java (+ GLSL for shaders).

Collinsions works perfectly before rotating. http://imgur.com/a/39Sya

But not after that http://imgur.com/a/TTxbQ


Answer:

I finally solved it. I got few new problems but at least this is done now.

Matrix4f rotationMin = Maths.createTransformationMatrix(min, rotX, rotY, rotZ, scale);

had to be replaced with

Matrix4f rotationMin = Maths.createTransformationMatrix(position, rotX, rotY, rotZ, scale);

And then add difference / 2 to max and subtract difference / 2 from min.