I'm attempting to create a game engine using LWJGL3 (Open GL 4.1, NVIDIA-10.8.14) in Java, but I seem to have run into a *small* problem...
When I attempt to translate the square, instead of actually moving as expected, it ends up stretching.
LWJGL doesn't have a math
class, so I had to create my own Matrix and TransformationMatrix classes. I've combined them into one snippet to reduce the size of this post.
My assumption is that the error lies somewhere in the TransformationMatrix's translate()
function
public TransformationMatrix translate(Vector3f vector){
super.m30 += ((super.m00 * vector.getX()) + (super.m10 * vector.getY()) + (super.m20 * vector.getZ()));
super.m31 += ((super.m01 * vector.getX()) + (super.m11 * vector.getY()) + (super.m21 * vector.getZ()));
super.m32 += ((super.m02 * vector.getX()) + (super.m12 * vector.getY()) + (super.m22 * vector.getZ()));
super.m33 += ((super.m03 * vector.getX()) + (super.m13 * vector.getY()) + (super.m23 * vector.getZ()));
return this;
}
I've played around with it, but I haven't been able to get any positive results from it.
public class Matrix4f {
public float
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33;
public FloatBuffer store(FloatBuffer buffer){
buffer.put(this.m00);
buffer.put(this.m10);
buffer.put(this.m20);
buffer.put(this.m30);
buffer.put(this.m01);
buffer.put(this.m11);
buffer.put(this.m21);
buffer.put(this.m31);
buffer.put(this.m02);
buffer.put(this.m12);
buffer.put(this.m22);
buffer.put(this.m32);
buffer.put(this.m03);
buffer.put(this.m13);
buffer.put(this.m23);
buffer.put(this.m33);
return buffer;
}
////////////////////////////
// //
// TRANSFORMATION MATRIX //
// //
////////////////////////////
public class TransformationMatrix extends Matrix4f{
public TransformationMatrix(Vector3f translation){
this.setIdentity();
this.translate(translation);
}
public TransformationMatrix setIdentity(){
super.m00 = 1.0f;
super.m01 = 0.0f;
super.m02 = 0.0f;
super.m03 = 0.0f;
super.m10 = 0.0f;
super.m11 = 1.0f;
super.m12 = 0.0f;
super.m13 = 0.0f;
super.m20 = 0.0f;
super.m21 = 0.0f;
super.m22 = 1.0f;
super.m23 = 0.0f;
super.m30 = 0.0f;
super.m31 = 0.0f;
super.m32 = 0.0f;
super.m33 = 1.0f;
return this;
}
public TransformationMatrix translate(Vector3f vector){
super.m30 += ((super.m00 * vector.getX()) + (super.m10 * vector.getY()) + (super.m20 * vector.getZ()));
super.m31 += ((super.m01 * vector.getX()) + (super.m11 * vector.getY()) + (super.m21 * vector.getZ()));
super.m32 += ((super.m02 * vector.getX()) + (super.m12 * vector.getY()) + (super.m22 * vector.getZ()));
super.m33 += ((super.m03 * vector.getX()) + (super.m13 * vector.getY()) + (super.m23 * vector.getZ()));
return this;
}
}
My vertex shader contains
#version 400 core
in vec3 position;
in vec2 texture;
out vec2 texture_coords;
uniform mat4 transformationMatrix;
void main(void){
gl_Position = transformationMatrix * vec4(position, 1.0);
texture_coords = texture;
}
And fragment shader contains
#version 400 core
//Variables...
void main(void){
out_color = texture(textureSampler, texture_coords);
}
The square is rendered using the following points and indices
//stored in the vertex shader's "position"
float[] positions = {
-0.5f,0.5f,0.0f,
-0.5f,-0.5f,0.0f,
0.5f,-0.5f,0.0f,
0.5f,0.5f,0.0f,
};
//bound using
//
//int id = GL15.glGenBuffers();
//GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, id);
//GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, toIntBuffer(indices), GL15.GL_STATIC_DRAW);
int[] indices = {
0,1,3,
3,1,2,
};
//stored in the vertex shader's "texture"
float[] textureCoords = {
0,0,
0,1,
1,1,
1,0,
};
and then translated using TransformationMatrix.translate()
(In the above gif, it's being translated by <0.0f, 0.01f, 0.0f>
). The square is rendered using
public void render(){
GL30.glBindVertexArray(modelID);
GL20.glEnableVertexAttribArray(0);
GL20.glEnableVertexAttribArray(1);
//position begins at <0, 0, 0>, and is incremented by <0, 0.01f, 0>
//every frame in the above gif
TransformationMatrix matrix = new TransformationMatrix(
position
);
//load the transformation matrix to the vertex shader
FloatBuffer buffer = matrix.store(BufferUtils.createFloatBuffer(16));
buffer.flip();
//location being the location of the "transformationMatrix" in the
//vertex shader
GL20.glUniformMatrix4fv(location, false, buffer);
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID));
//vertexCount is indices.length (6)
GL11.glDrawElements(GL11.GL_TRIANGLES, vertexCount, GL11.GL_UNSIGNED_INT, 0);
GL20.glDisableVertexAttribArray(1);
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
So far, I've tried playing around with TransformationMatrix.translate()
, and I've double, triple, and quadruple checked that I have the correct code in these classes.
One thing I noticed is that changing the translate()
method to add to m03
, m13
, and m23
instead of m30
, m31
, and m32
respectively makes the square begin translating up, scale down, and then begin translating down
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
is called before rendering each frame
It's more to do with your matrices. Remember that translations in 3D should be the 4th column of the 1st row in their x, y and z values. You're inputting each value more than once, which is of course going to alter the translation. You're overcomplicating transformations; all you need to do is get the location of your matrix of 4th col, 1st row, 2nd row and 3rd row and then increment their values based on the x, y and z values respectively. Using your matrix:
m00, m01, m02, m03,
m10, m11, m12, m13,
m20, m21, m22, m23,
m30, m31, m32, m33;
m03
, m13
and m23
would be where you want your translations for x
, y
and z
values respectively. To translate something 100 in x, 200 in y and 300 in z, you'd do this:
1.0, 0.0, 0.0, 100,
0.0, 1.0, 0.0, 200,
0.0, 0.0, 1.0, 300,
0.0, 0.0, 0.0, 1.0;
What your translation
function is doing, I do not know. It looks like you're creating a translation matrix and multiplying it by the current, which makes your code extremely unreadable. The fact of the matter is that this isn't required because your code already returns a matrix, which implies this kind of usage:
playerPosition = translate(playerPosition);
I don't see anything wrong with your rendering code, nor your shaders. So, it must have something to do with your matrices. It's just a case of establishing your majorness. Remember, OpenGL reads everything column major. So, that means OpenGL will read m00
then m10
then m20
etc. It creates its own matrix out of those values.