## Hot questions for Using Lightweight Java Game Library in matrix

Question:

I am trying to build a model matrix where I apply some common transformations like scaling, rotation and translation. I was following a tutorial here: https://solarianprogrammer.com/2013/05/22/opengl-101-matrices-projection-view-model/. I know that matrixes are not commutative so I think I paid attention to get the correct order of transformations.

But when I try to translate my model it gets stretched into the corresponding axis instead of translating. Here is how it looks:

I would appreciate any help. :)

Vertex shader

# version 330 in vec3 in_pos; in vec3 in_col; in vec3 in_norm; // uniform wie eine konstante, kann einmal per frame geaender werden und gillt fuer alle vertices uniform mat4 mvp; // mat4 = 4x4 Matrix // out stream to fragment shader out vec4 frag_pos; out vec4 frag_col; out vec4 frag_norm; // column major void main() { gl_Position = mvp * vec4(in_pos, 1.0); frag_pos = vec4(in_pos,1.0); // transparent frag_col = vec4(in_col,0.5); frag_col = vec4(in_col,1); frag_norm = vec4(in_norm,1.0); }

Java transformations

float aspectRatio = (float) HEIGHT /(float)WIDTH; float[][] rotX = new float[][] { {1.0f,0.0f,0.0f,0.0f}, {0.0f,(float)Math.cos(thetaX),(float)-Math.sin(thetaX),0.0f}, {0.0f,(float)Math.sin(thetaX),(float)Math.cos(thetaX),0.0f}, {0.0f,0.0f,0.0f,1.0f} }; float[][] rotY = new float[][] { {(float)Math.cos(thetaY),0.0f,(float)Math.sin(thetaY),0.0f}, {0.0f,1.0f,0.0f,0.0f}, {(float)-Math.sin(thetaY),0.0f,(float)Math.cos(thetaY),0.0f}, {0.0f,0.0f,0.0f,1.0f} }; float[][] rotZ = new float[][] { {(float)Math.cos(thetaZ),(float)-Math.sin(thetaZ),0.0f,0.0f}, {(float)Math.sin(thetaZ),(float)Math.cos(thetaZ),0.0f,0.0f}, {0.0f,0.0f,1.0f,0.0f}, {0.0f,0.0f,0.0f,1.0f} }; float[][] translation = new float[][] { {1.0f,0.0f,0.0f,transX}, {0.0f,1.0f,0.0f,transY}, {0.0f,0.0f,1.0f,transZ}, {0.0f,0.0f,0.0f,1.0f} }; float[][] scaleMatrix = new float[][] { {scale,0.0f,0.0f,0.0f}, {0.0f,scale,0.0f,0.0f}, {0.0f,0.0f,scale,0.0f}, {0.0f,0.0f,0.0f,1.0f} }; float[][] aspect = new float[][] { {aspectRatio,0.0f,0.0f,0.0f}, {0.0f,1.0f,0.0f,0.0f}, {0.0f,0.0f,1.0f,0.0f}, {0.0f,0.0f,0.0f,1.0f} }; float[][] rotationMatrix = Matrix.matMult(rotZ,Matrix.matMult(rotY,rotX)); float[][] matrix = Matrix.matMult(translation,Matrix.matMult(rotationMatrix,scaleMatrix)); float[] model = Matrix.matrixToFloatVector(Matrix.matMult(matrix,aspect)); FloatBuffer fb = BufferUtils.createFloatBuffer(16); fb.put(model); fb.flip(); // Reset pointer // "upload" to graphics card glUniformMatrix4fv(uLocMVP, false, fb);

Answer:

See The OpenGL Shading Language 4.6, 5.4.2 Vector and Matrix Constructors, page 110:

To initialize a matrix by specifying vectors or scalars, the components are assigned to the matrix elements in column-major order.

mat4(float, float, float, float, // first column float, float, float, float, // second column float, float, float, float, // third column float, float, float, float); // fourth column

This means you have to transpose the matrix before setting the uniform variable of type `mat4`

.

This can be done automatically by setting the 3rd parameter of `glUniformMatrix4fv`

`true`

:

glUniformMatrix4fv(uLocMVP, true, fb)

Instead you can transpose the initialization and swap the matrix multiplications (`Matrix.matMult`

), too.

Question:

I am using OpenGL for 2D rendering and would like to use actual pixel coordinates. By this, I mean that I would like (0,0) to be in the top left of the window, and (width,height) to be in the bottom right of the window (where width and height are the window's dimensions in pixels). To do this, I use a projection matrix which is generated with glOrtho, and then passed to a vertex shader:

GL11.glViewport(0, 0, width, height); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glMatrixMode(GL11.GL_PROJECTION); GL11.glOrtho(0f, width, height, 0f, -1f, 1f); GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projectionBuffer); GL11.glLoadIdentity();

I am using LWJGL, which does not have bindings for glm so I obtain a 2D orthographic matrix using the OpenGL calls above. I reset the projection matrix so it does not affect my later draw calls. After this, the `projectionBuffer`

, a `FloatBuffer`

, is filled with the projection matrix generated by `glOrtho`

.

The projection matrix produced looks like this (I don't know if this is helpful):

0.0015625 0.0 0.0 1.0 0.0 -0.0027777778 0.0 -1.0 0.0 0.0 -1.0 0.0 0.0 0.0 0.0 1.0

My vertex shader looks like this:

#version 330 core layout (location = 0) in vec4 vertex; out vec2 TexCoords; uniform mat4 model; uniform mat4 projection = mat4(1.0); void main() { TexCoords = vertex.zw; gl_Position = projection * model * vec4(vertex.x, vertex.y, 0.0, 1.0); }

When I initialise the shader, I use `glUniformMatrix4`

to set the projection matrix's value. I'm certain this is done successfully as when I use glGetUniform after, it returns the same projection matrix.

The model matrix is produced when every textured quad is to be drawn. Each quad shares the same vertices and uvs which are all stored in a single VBO in the same VAO. The vertex data is shared between each quad that is drawn, with a different model matrix applied to each. The model matrix is calculated correctly to produce real screen coordinates. For example, a square with its top left at (0,0) and width/height of 128 would produce the following model matrix:

128.0 0.0 0.0 0.0 0.0 128.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0

This model matrix is passed to the shader successfully using `glUniformMatrix4`

and I have checked this.

To initialise the shared VAO with the quad's vertex data, I use the following code:

vao = GL30.glGenVertexArrays(); int vbo = GL15.glGenBuffers(); GL30.glBindVertexArray(vao); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, QUAD_BUFFER, GL15.GL_STATIC_DRAW); GL20.glEnableVertexAttribArray(0); GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, 16, 0); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL30.glBindVertexArray(0);

`QUAD_BUFFER`

refers to a `float[]`

containing the vertex and texture coordinate data.

Finally, to draw a textured quad, I use the following:

shader.setMatrix4f("model", model);//Makes a call to glUniformMatrix4 - model is the model matrix as explained above. GL13.glActiveTexture(GL13.GL_TEXTURE0); texture.bind(); GL30.glBindVertexArray(vao); GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, 6);

The problem is, when I run the application, nothing is drawn on the window; it remains completely black. When not using the shader, I can draw shapes using the old method (`glBegin`

etc.). I cannot figure out what I'm doing wrong.

Answer:

I was able to solve the issue with great thanks to somebody over at the java-gaming forums. He pointed out to me that calling glOrtho modifies the existing matrix that is loaded so all I needed to do was call glLoadIdentity before making the glOrtho call. I have switched to using a java port of glm for these calculations now anyway.

Question:

I am getting an error when I try to set the Matrix mode of my LWJGL program to GL_Projection.

glMatrixMode(GL_PROJECTION);

The error is :

Exception in thread "main" java.lang.IllegalStateException: Function is not supported at org.lwjgl.BufferChecks.checkFunctionAddress(BufferChecks.java:58) at org.lwjgl.opengl.GL11.glMatrixMode(GL11.java:2075) ....

I have tracked down the error to when I make my Display. When I remove my ContexAttribs my code doesn't display the error and renders! ( when I comment out the code that needs the contexattribs )

This is my code:

display code:

Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT)); ContextAttribs attribs = new ContextAttribs(3, 2).withProfileCore(true).withForwardCompatible(true); Display.create(new PixelFormat().withDepthBits(24).withSamples(4), attribs); Display.setTitle(TITLE); Display.setInitialBackground(1, 1, 1); GL11.glEnable(GL13.GL_MULTISAMPLE); GL11.glViewport(0, 0, WIDTH, HEIGHT);

initialization method:

glMatrixMode(GL_PROJECTION); glOrtho(0, width, height, 0, -1, 1); glMatrixMode(GL_MODELVIEW); glClearColor(0, 1, 0, 0); textureID = loadTexture("res/hud.png"); glEnable(GL_TEXTURE_2D);

rendering method:

glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPushMatrix(); glTranslatef(0, 0, 0); glBindTexture(GL_TEXTURE_2D, textureID); glBegin(GL_QUADS); { glTexCoord2f(0, 0); glVertex2f(0, 0); glTexCoord2f(1, 0); glVertex2f(width, 0); glTexCoord2f(1, 1); glVertex2f(width, height); glTexCoord2f(0, 1); glVertex2f(0, height); } glEnd(); glPopMatrix();

Does anyone know how I could get this code working with the contextAttribs?

Thanks in advance!

Edit 1: I have all the functions and variables in GL11 statically imported.

Answer:

First of all, drawing with `glBegin`

/`glEnd`

sequences is deprecated since more than 10 years. See Vertex Specification for a state of the art way of rendering.

With the line

ContextAttribs attribs = new ContextAttribs(3, 2).withProfileCore(true).withForwardCompatible(true);

a OpenGL **core** profile Context with Forward compatibility bit set is generated.

In this context all the deprecated functions like `glBegin`

/`glEnd`

sequences, matrix stack (`glMatrixMode`

), the standard light model etc. are removed. This causes the error.

See also Fixed Function Pipeline and OpenGL Context

Skip the setting of the forward compatibility bit (`.withForwardCompatible(true)`

) to solve the issue.

Question:

I have a problem while trying to apply a projection matrix to an object in GLSL.

Here is the GLSL code:

#version 330 layout (location = 0) in vec3 pos; uniform mat4 transformationMatrix; uniform mat4 projectionMatrix; out vec4 originalPos; out vec4 transformedPos; out vec4 col; void main(){ col = vec4(pos, 1); originalPos = (projectionMatrix / transformationMatrix) * vec4(pos, 1); vec4 newPos = projectionMatrix * vec4(pos, 1); transformedPos = newPos; gl_Position = newPos; }

It works perfectly fine on Windows:

But it's not working on Linux:

I know for sure is some kind of problem related to the projection matrix because if I omit the matrix application, it works just fine.

It is the exact same code and shaders.

Using lwjgl and Java.

Answer:

This

originalPos = (projectionMatrix / transformationMatrix) * vec4(pos,1);

Makes no sense, for a vector transformation. The '/' operator, when applied in GLSL to matrices does a component-wide division. What you probably want through is inversion, which is an entirely different operation.

Question:

I am using a lookAt matrix calculated in an open source math library I found for LWJGL called JOML for free cam in my game. It works well when rotating left and right, but looking up and down seems to cause major distortions issues similar to heavily increasing the FOV.

Looking straight forward:

But when looking up:

And when looking down:

I haven't been able to find someone with a similar error, and no one using JOML has reported this. I'm not the best at matrix math, so all my tries at calculating my own lookAt matrix were fails. If someone could make a lookAt matrix using JOML, or say any one of my (most likely) possible errors, that would be much appreciated, thank you.

Answer:

Well, the lookAt code provided by that library is just this (I'm leaving the actual source code out and only keep the comments, as they nicely explain the steps which are done):

public final static void lookAt(Vector3f position, Vector3f centre, Vector3f up, Matrix4f dest) { // Compute direction from position to lookAt // Normalize direction // Normalize up // right = direction x up // up = right x direction // Set matrix elements }

And this code is just *wrong*. Interestingly, I've seen this mistake before. It is actually the same error that that the "official" `gluLookAt()`

manpage still contains (the actual glu implementations do not have the error, just that documentation is wrong).

What this code does is building an orthonormal basis. And the problem is that the up vector is normalized *before* the cross product for calculating `right`

. The assumption seems to be that when building the cross product of two unit length vectors, the result will also be a unit lenght vector. But that is a common misconception. What's actually holding true is just:

length( cross( a, b) ) == lenght(a) * length(b) * sin(alpha)

where alpha is the angle between `a`

and `b`

. So the unit lenght assumption only holds *if the vectors are already orthogonal*. As the vectors are never re-normalized after the cross product, the resulting basis is not orthonormal, but will introduce some non-uniform scaling. The `lookAt`

assumes that the inverse rotation can be calculated by the transposed matrix, which will completely fail in this case.

The distortion you see will get more severe when the angle between the viewing direction and your up vector will move away from 90 degrees.

The correct way to deal with this is just doing the normalization at a different point. Don't normalize the up-vector before the cross-product, but normalize it's result instead. Then, you have two unit-lenght vectors orthogonal to each other, and the second cross-product will also work as expected. So the actual lookAt function should be:

// Compute direction from position to lookAt // Normalize direction // right = direction x up // Normalize right // up = right x direction // Set matrix elements

Question:

I've been running into a problem with a 3D engine I've been working on. The translation applied by the transformation matrix isn't correct, and frankly, I have no idea why.

The transformation matrix I am using:

{ 1f, 0, 0, 0, 0, 1f, 0, 1f, 0, 0, 1f, 0, 0, 0, 0, 1f }

This transformation matrix is applied to a normal cube consisting of two triangles, which then warps the edges of the cube instead of applying a translation.

Original rectangle:

Warped rectangle:

P.S. Any translations on the z-axes(near/far) works properly, only the x-(left/right), and y-axes(up/down) warp the cube.

Answer:

Im new to OpenGL, but as far as I know, matrices in OpenGL are represented using column-major matrix ordering: That means you should use the transposed transformation matrix:

{ 1f, 0, 0, 0, 0, 1f, 0, 0, 0, 0, 1f, 0, 0, 1f, 0, 1f }

Source: https://stackoverflow.com/a/13294326/6163527

Question:

I'm trying to transform 3d point coordinates to the 2d screen coordinates.

But, the problem is when I implemented it and run the program, even though I change the camera position, there was no change on the output. Output generally out of the screen coordinates range even though I can completely see the 3d model(so no chance of any point or triangle to go out of the screen coordinates).

Method that convers the 3d coordinates to 2d ones:

public Vector2f get2DFrom3D(float x, float y, float z) { FloatBuffer screen_coords = BufferUtils.createFloatBuffer(4); IntBuffer viewport = BufferUtils.createIntBuffer(16); FloatBuffer modelview = BufferUtils.createFloatBuffer(16); FloatBuffer projection = BufferUtils.createFloatBuffer(16); GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview); System.out.println("modelview:"); displayFloatBuffer(modelview); GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection); System.out.println("projection:"); displayFloatBuffer(projection); GL11.glGetInteger(GL11.GL_VIEWPORT, viewport); System.out.println("viewport:"); displayIntBuffer(viewport); boolean result = GLU.gluProject(x, y, z, modelview, projection, viewport, screen_coords); if (result) { System.out.printf("Convert [ %6.2f %6.2f %6.2f ] -> Screen [ %4d %4d ]\n", x, y, z, (int)screen_coords.get(0), (int)(screen_coords.get(3)-screen_coords.get(1))); return new Vector2f((int)screen_coords.get(0), (int)(screen_coords.get(3)-screen_coords.get(1))); } else { return null; } }

Projection matrix is created via this method and directly loaded into the vertex shader:

private void createProjectionMatrix(){ float aspectRatio = (float) Display.getWidth() / (float) Display.getHeight(); float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio); float x_scale = y_scale / aspectRatio; float frustum_length = FAR_PLANE - NEAR_PLANE; projectionMatrix = new Matrix4f(); projectionMatrix.m00 = x_scale; projectionMatrix.m11 = y_scale; projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustum_length); projectionMatrix.m23 = -1; projectionMatrix.m32 = -((2 * NEAR_PLANE * FAR_PLANE) / frustum_length); projectionMatrix.m33 = 0; }

Vertex shader:

#version 400 core in vec3 position; uniform mat4 transformationMatrix; uniform mat4 projectionMatrix; uniform mat4 viewMatrix; void main(void) { vec4 worldPosition = transformationMatrix * vec4(position, 1); gl_Position = projectionMatrix * viewMatrix * worldPosition; }

Renderer:

public void render(Entity entity, int displayMode) { RawModel model = entity.getModel(); shader.start(); GL30.glBindVertexArray(model.getVaoID()); GL20.glEnableVertexAttribArray(0); GL20.glEnableVertexAttribArray(1); Matrix4f transformationMatrix = Maths.createTransformationMatrix(entity.getPosition(), entity.getRotX(), entity.getRotY(), entity.getRotZ(), entity.getScale()); shader.loadTransformationMatrix(transformationMatrix); GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexAmount(), GL11.GL_UNSIGNED_INT, 0); GL20.glDisableVertexAttribArray(0); GL20.glDisableVertexAttribArray(1); GL30.glBindVertexArray(0); shader.stop(); }

Then, I debug the code and see that:

GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelView); GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);

These lines are just not taking real matrix values that I defined. They are just order 4 identity matrices. Then, I searched for glMatrixMode to set that matrices or set gl_ModelViewMatrix, but it turns out that the opengl version I use is not supporting those anymore.

So, I think the problem is I somehow related to those variables and I somehow need to set them. Last but not least, here is the output for the get2DFrom3D method:

modelview: 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 projection: 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 1.0 viewport: 0 0 1209 891 0 0 0 0 0 0 0 0 0 0 0 0

I think only viewport is correct but modelview and projection matrices are looking like they're not loading the values calculated for them.

Note: I'm currently using lwjgl 2.9.3.

Answer:

I found a solution to my problem and it works fine. Here is my new get2DFrom3D method:

public Vector2f get2DFrom3D(float x, float y, float z) { FloatBuffer screen_coords = BufferUtils.createFloatBuffer(4); IntBuffer viewport = BufferUtils.createIntBuffer(16); FloatBuffer modelview = BufferUtils.createFloatBuffer(16); FloatBuffer projection = BufferUtils.createFloatBuffer(16); Matrix4f modelviewMatrix = new Matrix4f(); Matrix4f transformationMatrix = new Matrix4f(); Matrix4f.mul(transformationMatrix , Maths.createViewMatrix(camera) , modelviewMatrix); modelviewMatrix.store(modelview); modelview.rewind(); projectionMatrix.store(projection); projection.rewind(); GL11.glGetInteger(GL11.GL_VIEWPORT, viewport); boolean result = GLU.gluProject(x, y, z, modelview, projection, viewport, screen_coords); if (result) { Vector2f vector = new Vector2f((int)screen_coords.get(0), (int)(screen_coords.get(1))); return vector; } else { return null; } }

Instead of using glGetFloat method to get modelview and projection matrices. I instead used the matrices I already created in my other classes and pass those matrices as parameters. Then, I convert the matrices to buffers so that I can use them. After rewinding them, I finally be able to get the correct screen coordinates of a point from gluProject.

Even though problem is solved, I still don't know why these lines did't work:

GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modelview); System.out.println("modelview:"); displayFloatBuffer(modelview); GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, projection);

Question:

I need help with calculating the lookAt method Here is my method

public void lookAt(Vector3f position, Vector3f direction, Vector3f up) { Vector3f f = new Vector3f(); Vector3f u = new Vector3f(); Vector3f s = new Vector3f(); Vector3f.sub(direction, position, f); f.normalise(f); up.normalise(u); Vector3f.cross(f, u, s); s.normalise(s); Vector3f.cross(s, f, u); this.setIdentity(); this.m00 = s.x; this.m10 = s.y; this.m20 = s.z; this.m01 = u.x; this.m11 = u.y; this.m21 = u.z; this.m02 = -f.x; this.m12 = -f.y; this.m22 = -f.z; this.m30 = -Vector3f.dot(s, position); this.m31 = -Vector3f.dot(u, position); this.m32 = Vector3f.dot(f, position); }

but when I test it like this `camera.lookAt(position, new Vector3f(1, 0 ,0), new Vector3f(0, -1, 0));`

my camera is looking down, end if only i do this `camera.lookAt(position, new Vector3f(10000, 0 ,0), new Vector3f(0, -1, 0));`

, camera is looking forward. Can you help please ?

P.S. sorry for my english

Answer:

The second parameter of a lookAt function is usually not the direction in which you want to look but the point which you want to look at. As far as I can see, the calculation of your method also expects a second point and not a direction (which is calculated from the two point and stored in f).

In conclusion, the results you get look correct to me, except that you passed the wrong parameters to the function.

**How to create a function that creates a matrix from a point and a direction**

The rotation required is one that maps the minus z-direction onto the view vector. Additionally, we want the x-vector to be mapped such that it is perpendicular to the plane spanned by view and up vector. This can relatively easy be achieved by writing -view in the third row of the 3x3 matrix. The other two vectors can then be computed as the cross products of view and up vector which results in a right-vector onto which the x-axis should be mapped. The last vector (the target mapping for the y-axis) is then computed by the cross product of view and right vector. The cross products for the last vector is used, since we know that all rotation matrices have base vectors that are perpendicular:

viewv = normalize(-view) rightv = normalize(cross(view, up)) upv = normalize(cross(view, up)) -- rightv -- rotation_matrix = [ -- upv -- ] -- viewv --

When the camera is located in the origin, then we are done now. But since this is in general not the case, we have to add a translation part that transforms the scene such that the camera is the origin. Thus `t = -camera`

.

The final matrix is now composed by first translating the space and then rotating it according to our calculated matrix:

lookat_matrix = rotation_matrix * translate(-camera)

Since it is fairly late here and depending on the notation you use it might be that the rotation matrix has to be transposed and that some signs have to be adjusted.

Question:

I have been trying to make a game engine, just to learn some new things, everything was working but I then decided that for lighting I would make my own matrices classes and pass the final matrix to the shader. Unfortunately this ended up breaking the whole rendering system as everything now stretches and is definitely not right. Currently I have messed around with the order of multiplication but it still doesn't work, I have had this problem for over a month and just want to fix it and continue making the engine.

Firstly, here is the default shader used on PC:

public static final String[] pcVertexShaderCode = new String[] { "attribute vec4 andor_vertexPosition;", "attribute vec3 andor_normal;", "attribute vec2 andor_vtextureCoord;", "attribute vec4 andor_vcolour;", "uniform mat4 andor_modelmatrix;", "uniform mat4 andor_viewmatrix;", "uniform mat4 andor_projectionmatrix;", "uniform mat4 andor_modelviewprojectionmatrix;", "varying vec4 andor_colour;", "varying vec2 andor_textureCoord;", "void andor_main();", "void main() {", " andor_colour = andor_vcolour;", " andor_textureCoord = andor_vtextureCoord;", // " andor_modelviewprojectionmatrix = andor_modelmatrix * andor_viewmatrix * andor_projectionmatrix;", " gl_Position = andor_modelviewprojectionmatrix * andor_vertexPosition;", " andor_main();", "}" }; public static final String[] pcFragmentShaderCode = new String[] { "uniform sampler2D andor_texture;", "uniform float andor_hasTexture;", "varying vec4 andor_colour;", "varying vec2 andor_textureCoord;", "void andor_main();", "void main() {", " if (andor_hasTexture > 0.5) {", " gl_FragColor = andor_colour * texture2D(andor_texture, andor_textureCoord);", " } else {", " gl_FragColor = andor_colour;", " }", " andor_main();", "}" };

This is the render method which is called by each object in the scene:

/* The method used to draw the object */ public void render() { //Multiply the matrices together Matrix4D projectionViewMatrix = Matrix.multiply(Matrix.projectionMatrix, Matrix.viewMatrix); Matrix.modelViewProjectionMatrix = (Matrix.multiply(projectionViewMatrix, Matrix.modelMatrix)); //Sky box in relatively the right place // Matrix4D modelViewMatrix = Matrix.multiply(Matrix.modelMatrix, Matrix.viewMatrix); // Matrix.modelViewProjectionMatrix = (Matrix.multiply(modelViewMatrix, Matrix.projectionMatrix)); // Matrix4D modelProjectionMatrix = Matrix.multiply(Matrix.modelMatrix, Matrix.projectionMatrix); // Matrix.modelViewProjectionMatrix = (Matrix.multiply(Matrix.viewMatrix, modelProjectionMatrix)); // Matrix4D modelViewMatrix = Matrix.multiply(Matrix.modelMatrix, Matrix.viewMatrix); // Matrix.modelViewProjectionMatrix = (modelViewMatrix); // //TEST // for (int a = 0; a < 16; a++) { // //2D WORKS!!!!! Kind of... // Matrix.modelViewProjectionMatrix.values[a] = Matrix.modelMatrix.values[a] * Matrix.projectionMatrix.values[a] * Matrix.viewMatrix.values[a]; // } System.out.println(Matrix.modelViewProjectionMatrix.toString() + "\n"); //Set the correct android shader Shader shader = defaultShader; if (currentShader != null) shader = currentShader; //Use the shader program GL20.glUseProgram(shader.program); //Enable the arrays as needed int vertexPositionAttribute = shader.getAttributeLocation("andor_vertexPosition"); int normalAttribute = 0; int colourAttribute = 0; int texturesAttribute = 0; int modelMatrixAttribute = shader.getUniformLocation("andor_modelmatrix"); int viewMatrixAttribute = shader.getUniformLocation("andor_viewmatrix"); int projectionMatrixAttribute = shader.getUniformLocation("andor_projectionmatrix"); int matrixAttribute = shader.getUniformLocation("andor_modelviewprojectionmatrix"); GL20.glUniformMatrix4(modelMatrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.modelMatrix.getValues())); GL20.glUniformMatrix4(viewMatrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.viewMatrix.getValues())); GL20.glUniformMatrix4(projectionMatrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.projectionMatrix.getValues())); GL20.glUniformMatrix4(matrixAttribute, false, BufferUtils.createFlippedBuffer(Matrix.modelViewProjectionMatrix.getValues())); GL20.glEnableVertexAttribArray(vertexPositionAttribute); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.verticesHandle); GL20.glVertexAttribPointer(vertexPositionAttribute, this.vertexValuesCount, GL11.GL_FLOAT, false, 0, 0); if (this.normalsData != null) { normalAttribute = shader.getAttributeLocation("andor_normal"); GL20.glEnableVertexAttribArray(normalAttribute); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.normalsHandle); GL20.glVertexAttribPointer(normalAttribute, this.vertexValuesCount, GL11.GL_FLOAT, false, 0, 0); } if (this.colourData != null) { colourAttribute = shader.getAttributeLocation("andor_vcolour"); GL20.glEnableVertexAttribArray(colourAttribute); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.coloursHandle); GL20.glVertexAttribPointer(colourAttribute, this.colourValuesCount, GL11.GL_FLOAT, false, 0, 0); } if (this.textureData != null) { texturesAttribute = shader.getAttributeLocation("andor_vtextureCoord"); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.texturesHandle); GL20.glEnableVertexAttribArray(texturesAttribute); GL20.glVertexAttribPointer(texturesAttribute, this.textureValuesCount, GL11.GL_FLOAT, false, 0, 0); GL20.glUniform1i(shader.getUniformLocation("andor_texture"), 0); if (texture != null) GL20.glUniform1f(shader.getUniformLocation("andor_hasTexture"), 1f); } if (this.drawOrder != null) { GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.drawOrderHandle); GL11.glDrawElements(this.renderMode, this.drawOrder.length, GL11.GL_UNSIGNED_SHORT, 0); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); } else { //Draw the arrays GL11.glDrawArrays(this.renderMode, 0, this.verticesData.length / this.vertexValuesCount); } //Disable the arrays as needed if (this.normalsData != null) GL20.glDisableVertexAttribArray(normalAttribute); if (this.textureData != null) GL20.glDisableVertexAttribArray(texturesAttribute); if (this.colourData != null) GL20.glDisableVertexAttribArray(colourAttribute); GL20.glDisableVertexAttribArray(vertexPositionAttribute); //Stop using the shader program GL20.glUseProgram(0); }

Then here are the methods used to apply the translations to an object in the scene before rendering:

/* The method used to update the current view matrix */ public void updateViewMatrix() { //Get the position Vector3D p = this.getPosition(); //Get the rotation Vector3D r = this.getRotation(); //Get the scale Vector3D s = this.getScale(); //Save the current matrix clone = Arrays.copyOf(Matrix.modelMatrix.getValues(), 16); //Scale by the specified amount Matrix.modelMatrix = Matrix.scale(Matrix.modelMatrix, s); //Rotate by the specified amount Matrix.modelMatrix = Matrix.rotate(Matrix.modelMatrix, r.x, 1, 0, 0); Matrix.modelMatrix = Matrix.rotate(Matrix.modelMatrix, r.y, 0, 1, 0); Matrix.modelMatrix = Matrix.rotate(Matrix.modelMatrix, r.z, 0, 0, 1); //Move to the correct position Matrix.modelMatrix = Matrix.translate(Matrix.modelMatrix, p); } /* The method used to restore the current view matrix */ public void restoreViewMatrix() { //Restore the current matrix Matrix.modelMatrix.values = clone; }

In a camera class this is used to move the players view:

//Get the rotation Vector3D r = this.getRotation(); //Get the position Vector3D p = this.getPosition(); //Get the scale Vector3D s = this.getScale(); //Rotate by the specified amount Matrix.viewMatrix = Matrix.rotate(Matrix.viewMatrix, r.x, 1, 0, 0); Matrix.viewMatrix = Matrix.rotate(Matrix.viewMatrix, r.y, 0, 1, 0); Matrix.viewMatrix = Matrix.rotate(Matrix.viewMatrix, r.z, 0, 0, 1); //Move to the correct position Matrix.viewMatrix = Matrix.translate(Matrix.viewMatrix, p); //Scale by the correct amount Matrix.viewMatrix = Matrix.scale(Matrix.viewMatrix, s);

Here is the Matrix4D class:

public class Matrix4D { /* The values within this matrix */ public float[] values; /* The default constructor */ public Matrix4D() { //Create the values this.values = new float[16]; } /* The constructor with the values given */ public Matrix4D(float[] values) { //Create the values this.values = values; } /* The constructor with the values given */ public Matrix4D(float[][] values) { //Load the values load(values); } /* The method used to set the values given a 2 dimensional array */ public void load(float[][] values) { this.values = new float[] { values[0][0], values[0][1], values[0][2], values[0][3], values[1][0], values[1][1], values[1][2], values[1][3], values[2][0], values[2][1], values[2][2], values[2][3], values[3][0], values[3][1], values[3][2], values[3][3] }; } /* The method used to get a value using the coordinate within this matrix */ public float get(int x, int y) { //Get the position int position = x + (y * 4); //Return the value return this.values[position]; } /* The method used to return a string representation of this matrix */ public String toString() { //Return the string return "[ " + this.values[0] + " " + this.values[1] + " " + + this.values[2] + " " + + this.values[3] + " ]" + "\n" + "[ " + this.values[4] + " " + this.values[5] + " " + + this.values[6] + " " + + this.values[7] + " ]" + "\n" + "[ " + this.values[8] + " " + this.values[9] + " " + + this.values[10] + " " + + this.values[11] + " ]" + "\n" + "[ " + this.values[12] + " " + this.values[13] + " " + + this.values[14] + " " + + this.values[15] + " ]"; } /* The method used to get the values */ public float[] getValues() { return this.values; } /* The method used to get the values in a 2D array */ public float[][] getValues2DArray() { //The array float[][] array = new float[4][4]; //Go through each value int column = 0; int row = 0; while (column * row < array.length) { row ++; if (row >= 4) { column++; row = 0; } array[column][row] = this.values[column * row]; } //Return the array return array; } }

Here is the Matrix class:

package org.andor.core; public class Matrix { /* The different matrices */ public static Matrix4D modelMatrix = new Matrix4D(); public static Matrix4D viewMatrix = new Matrix4D(); public static Matrix4D projectionMatrix = new Matrix4D(); public static Matrix4D modelViewProjectionMatrix = new Matrix4D(); /* The static method used to load an identity matrix */ public static void loadIdentity(Matrix4D matrix) { //Load the identity matrix matrix.load(new float[][] { new float[] { 1, 0, 0, 0 }, new float[] { 0, 1, 0, 0 }, new float[] { 0, 0, 1, 0 }, new float[] { 0, 0, 0, 1 }, }); } /* The static method used to add two matrices together */ public static Matrix4D add(Matrix4D matrixA, Matrix4D matrixB) { //Create a new matrix Matrix4D matrix = new Matrix4D(); //Go through each value for (int a = 0; a < matrix.values.length; a++) //Assign the current value matrix.values[a] = matrixA.values[a] + matrixB.values[a]; //Return the matrix return matrix; } /* The static method used to subtract a matrix (B) from another (A) */ public static Matrix4D subtract(Matrix4D matrixA, Matrix4D matrixB) { //Create a new matrix Matrix4D matrix = new Matrix4D(); //Go through each value for (int a = 0; a < matrix.values.length; a++) //Assign the current value matrix.values[a] = matrixB.values[a] - matrixA.values[a]; //Return the matrix return matrix; } /* The static method used to multiply two matrices together */ public static Matrix4D multiply(Matrix4D matrixA, Matrix4D matrixB) { //Create a new matrix Matrix4D matrix = new Matrix4D(new float[][] { new float[] { (matrixA.values[0] * matrixB.values[0]) + (matrixA.values[1] * matrixB.values[4]) + (matrixA.values[2] * matrixB.values[8]) + (matrixA.values[3] * matrixB.values[12]), (matrixA.values[0] * matrixB.values[1]) + (matrixA.values[1] * matrixB.values[5]) + (matrixA.values[2] * matrixB.values[9]) + (matrixA.values[3] * matrixB.values[13]), (matrixA.values[0] * matrixB.values[2]) + (matrixA.values[1] * matrixB.values[6]) + (matrixA.values[2] * matrixB.values[10]) + (matrixA.values[3] * matrixB.values[14]), (matrixA.values[0] * matrixB.values[3]) + (matrixA.values[1] * matrixB.values[7]) + (matrixA.values[2] * matrixB.values[11]) + (matrixA.values[3] * matrixB.values[15]) }, new float[] { (matrixA.values[4] * matrixB.values[0]) + (matrixA.values[5] * matrixB.values[4]) + (matrixA.values[6] * matrixB.values[8]) + (matrixA.values[7] * matrixB.values[12]), (matrixA.values[4] * matrixB.values[1]) + (matrixA.values[5] * matrixB.values[5]) + (matrixA.values[6] * matrixB.values[9]) + (matrixA.values[7] * matrixB.values[13]), (matrixA.values[4] * matrixB.values[2]) + (matrixA.values[5] * matrixB.values[6]) + (matrixA.values[6] * matrixB.values[10]) + (matrixA.values[7] * matrixB.values[14]), (matrixA.values[4] * matrixB.values[3]) + (matrixA.values[5] * matrixB.values[7]) + (matrixA.values[6] * matrixB.values[11]) + (matrixA.values[7] * matrixB.values[15]) }, new float[] { (matrixA.values[8] * matrixB.values[0]) + (matrixA.values[9] * matrixB.values[4]) + (matrixA.values[10] * matrixB.values[8]) + (matrixA.values[11] * matrixB.values[12]), (matrixA.values[8] * matrixB.values[1]) + (matrixA.values[9] * matrixB.values[5]) + (matrixA.values[10] * matrixB.values[9]) + (matrixA.values[11] * matrixB.values[13]), (matrixA.values[8] * matrixB.values[2]) + (matrixA.values[9] * matrixB.values[6]) + (matrixA.values[10] * matrixB.values[10]) + (matrixA.values[11] * matrixB.values[14]), (matrixA.values[8] * matrixB.values[3]) + (matrixA.values[9] * matrixB.values[7]) + (matrixA.values[10] * matrixB.values[11]) + (matrixA.values[11] * matrixB.values[15]) }, new float[] { (matrixA.values[12] * matrixB.values[0]) + (matrixA.values[13] * matrixB.values[4]) + (matrixA.values[14] * matrixB.values[8]) + (matrixA.values[15] * matrixB.values[12]), (matrixA.values[12] * matrixB.values[1]) + (matrixA.values[13] * matrixB.values[5]) + (matrixA.values[14] * matrixB.values[9]) + (matrixA.values[15] * matrixB.values[13]), (matrixA.values[12] * matrixB.values[2]) + (matrixA.values[13] * matrixB.values[6]) + (matrixA.values[14] * matrixB.values[10]) + (matrixA.values[15] * matrixB.values[14]), (matrixA.values[12] * matrixB.values[3]) + (matrixA.values[13] * matrixB.values[7]) + (matrixA.values[14] * matrixB.values[11]) + (matrixA.values[15] * matrixB.values[15]) } }); //Return the matrix return matrix; } /* The static method used to transpose a matrix */ public static Matrix4D transpose(Matrix4D matrix) { //Get the values from the matrix float[][] values = matrix.getValues2DArray(); //The new values float[][] newValues = new float[4][4]; //Go through the array for (int y = 0; y < values.length; y++) { for (int x = 0; x < values[y].length; x++) { //Assign the new value newValues[x][y] = values[y][x]; } } //Return the matrix return new Matrix4D(newValues); } /* The static method used to translate a matrix */ public static Matrix4D translate(Matrix4D matrix, Vector3D vector) { //The transform matrix Matrix4D transform = new Matrix4D(new float[][] { new float[] { 1, 0, 0, vector.x }, new float[] { 0, 1, 0, vector.y }, new float[] { 0, 0, 1, vector.z }, new float[] { 0, 0, 0, 1 }, }); //Add onto the matrix and return the result return multiply(matrix, transform); } /* The static method used to rotate a matrix */ public static Matrix4D rotate(Matrix4D matrix, float angle, int x, int y, int z) { //The transform matrix Matrix4D transform = new Matrix4D(); //Calculate the values needed float cos = (float) Math.cos(angle); float sin = (float) Math.sin(angle); //Check the x y and z values if (x == 1) { transform.load(new float[][] { new float[] { 1, 0, 0, 0 }, new float[] { 0, cos, -sin, 0 }, new float[] { 0, sin, cos, 0 }, new float[] { 0, 0, 0, 1 }, }); } else if (y == 1) { transform.load(new float[][] { new float[] { cos, 0, sin, 0 }, new float[] { 0, 1, 0, 0 }, new float[] { -sin, 0, cos, 0 }, new float[] { 0, 0, 0, 1 }, }); } else if (z == 1) { transform.load(new float[][] { new float[] { cos, -sin, 0, 0 }, new float[] { sin, cos, 0, 0 }, new float[] { 0, 0, 1, 0 }, new float[] { 0, 0, 0, 1 }, }); } //Add onto the matrix and return the result return multiply(matrix, transform); } /* The static method used to scale a matrix */ public static Matrix4D scale(Matrix4D matrix, Vector3D vector) { //The transform matrix Matrix4D transform = new Matrix4D(new float[][] { new float[] { vector.x, 0, 0, 0 }, new float[] { 0, vector.y, 0, 0 }, new float[] { 0, 0, vector.z, 0 }, new float[] { 0, 0, 0, 1 }, }); //Add onto the matrix and return the result return multiply(matrix, transform); } /* The static method used to return an orthographic projection matrix */ public static Matrix4D ortho(float left, float right, float bottom, float top, float zfar, float znear) { // Matrix4D mat = new Matrix4D(); // mat.values[0] = 2 / (right - left); // mat.values[5] = 2 / (top - bottom); // mat.values[10] = - 2 / (zfar - znear); // mat.values[12] = - (right + left) / (right - left); // mat.values[13] = -(top + bottom) / (top - bottom); // mat.values[14] = -(zfar + znear) / (zfar - znear); // return mat; return new Matrix4D(new float[][] { new float[] { 2 / (right - left), 0, 0, -((right + left) / (right - left)) }, new float[] { 0, 2 / (top - bottom), 0, -((top + bottom) / (top - bottom)) }, new float[] { 0, 0, -2 / (zfar - znear), -((zfar + znear) / (zfar - znear)) }, new float[] { 0, 0, 0, 1 }, }); } /* The static method used to return a perspective projection matrix */ public static Matrix4D perspective(float fov, float aspect, float zNear, float zFar) { //Calculate the values that need to be calculated the most frequently float f = 1.0f / (float) Math.tan(fov / 2 * (Math.PI / 360.0)); float rangeReciprocal = 1.0f / (zNear - zFar); Matrix4D matrix = new Matrix4D(); //Set the matrix values matrix.values[0] = f / aspect; matrix.values[1] = 0.0f; matrix.values[2] = 0.0f; matrix.values[3] = 0.0f; matrix.values[4] = 0.0f; matrix.values[5] = f; matrix.values[6] = 0.0f; matrix.values[7] = 0.0f; matrix.values[8] = 0.0f; matrix.values[9] = 0.0f; matrix.values[10] = (zFar + zNear) * rangeReciprocal; matrix.values[11] = -1.0f; matrix.values[12] = 0.0f; matrix.values[13] = 0.0f; matrix.values[14] = 2.0f * zFar * zNear * rangeReciprocal; matrix.values[15] = 0.0f; return matrix; } }

This is what the current output looks like:

EDIT: I have since tried changing the multiplication just before giving the matrix to the shader to not use matrix multiplication as an experiment. This results in making 2D appear to wok properly however rotation seems to make the object get smaller, it then flips and then it will get bigger again but at this point it is inverted.

Answer:

There were two things that made this effect happen.

- The matrix needed to be transposed before using the matrix in the shader
- The perspective matrix wont work unless the znear value is 1 or over