OpenGL 2.0 Matrices not working - java

I am using Java and OpenGL (LWJGL) to setup some matrices, I didn't want to use the inbuilt methods as I also want this to work on Android and so using LWJGL's Matrix classes wouldn't be appropriate. Currently I am setting up a perspective view, using an fov of 70, znear 0.1, zfar 1000. Rotating using the current setup only results in strange results, not rotating in the correct way, and objects being scaled strangely and often disappearing.
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 (Used to setup and perform calculations on a matrix):
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[] { 0, 0, 0, vector.x },
new float[] { 0, 0, 0, vector.y },
new float[] { 0, 0, 0, vector.z },
new float[] { 0, 0, 0, 0 },
});
//Add onto the matrix and return the result
return add(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[] { 0, 0, 0, 0 },
new float[] { 0, cos, -sin, 0 },
new float[] { 0, sin, cos, 0 },
new float[] { 0, 0, 0, 0 },
});
} else if (y == 1) {
transform.load(new float[][] {
new float[] { cos, 0, sin, 0 },
new float[] { 0, 0, 0, 0 },
new float[] { -sin, 0, cos, 0 },
new float[] { 0, 0, 0, 0 },
});
} else if (z == 1) {
transform.load(new float[][] {
new float[] { cos, -sin, 0, 0 },
new float[] { sin, cos, 0, 0 },
new float[] { 0, 0, 0, 0 },
new float[] { 0, 0, 0, 0 },
});
}
//Add onto the matrix and return the result
return add(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, 0 },
});
//Add onto the matrix and return the result
return add(matrix, transform);
}
/* The static method used to return an orthographic projection matrix */
public static Matrix4D ortho(float left, float right, float top, float bottom, float zfar, float znear) {
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) {
float f = (float) (1f / Math.tan(fov / 2f));
return new Matrix4D(new float[][] {
new float[] { f / aspect, 0, 0, 0 },
new float[] { 0, f, 0, 0 },
new float[] { 0, 0, (zFar + zNear) / (zFar - zNear), (2 * zFar * zNear) / (zNear - zFar) },
new float[] { 0, 0, -1, 0 },
});
}
}
Finally the method used to setup the perspective/orthographic projection is:
/* The static method to setup an orthographic view given the width, height
* znear and zfar values */
public static void setupOrtho(float width, float height, float znear , float zfar) {
Matrix.loadIdentity(Matrix.modelMatrix);
Matrix.loadIdentity(Matrix.viewMatrix);
Matrix.projectionMatrix = Matrix.ortho(0, width, 0, height, znear, zfar);
}
/* The static method used to setup a perspective view given the
* fov, z near and z far value */
public static void setupPerspective(float fov, float zNear, float zFar) {
setupPerspective(fov, (float) (Settings.Window.Width / Settings.Window.Height), zNear, zFar);
}
/* The static method used to setup a perspective view given the
* fov, aspect ratio, z near and z far values */
public static void setupPerspective(float fov, float aspect, float zNear, float zFar) {
Matrix.loadIdentity(Matrix.modelMatrix);
Matrix.loadIdentity(Matrix.viewMatrix);
Matrix.projectionMatrix = Matrix.perspective(fov, aspect, zNear, zFar);
}
To render all of this and pass the matrices to the shader I am using
//Multiply the matrices together
Matrix4D modelViewMatrix = Matrix.multiply(Matrix.modelMatrix, Matrix.viewMatrix);
Matrix.modelViewProjectionMatrix = (Matrix.multiply(modelViewMatrix, Matrix.projectionMatrix));
System.out.println(Matrix.modelViewProjectionMatrix.toString() + "\n");
And in the shader I multiply the current vertices position by the model view projection marix.
Here is a picture of what it currently looks like.

You seem to be multiplying your matrices in the wrong order. When combining matrix transformations, the one on the right of the equation will be the first transformation performed.
You calculate your matrix as Model × View × Projection. When multiplying this by a vector, the projection would be performed first, followed by the view transformation, and lastly the model transformation. Obviously this is not what you want.
Your final matrix should be calculated like Projection × View × Model to do the transformations in the right order.

Related

Transformation Hierarchy (Problem with modelling transformation of parent on children), OpenGL, LWJGL 3

I am currently building a scene graph in OpenGL and I trying to model parent transformations on their children, but it does not seem to be working. :(
My main problem is that the child's rotation does not follow the parent's rotation correctly. The child rotates around the parent object (center), which is what it is supposed to do, but the speed of it orbiting around the parent increases as the parent's rotation increase and slows back down once the parent does a full circle (when the parent's rotation reaches back to the rotation it started of at). Also I did have some problems with the correct translation of the child in relation to the parent, but I managed to temporarily fix them (I think). More info in the code. Sorry for the lengthy explanation, I would have definitely attached a video if I could, of the problem.
This is my transform class, where the transformations are set, particularly the getTransformation() method:
public class Transform {
private Vector3f position, lastPosition;
private Vector3f rotation, lastRotation;
private Vector3f scale, lastScale;
private Transform parent;
private Matrix4f parentMatrix;
public Transform() {
this(null, null, null);
}
public Transform(Vector3f position, Vector3f rotation, Vector3f scale) {
this.position = position != null ? position : new Vector3f(0, 0, 0);
this.rotation = rotation != null ? rotation : new Vector3f(0, 0, 0);
this.scale = scale != null ? scale : new Vector3f(1, 1, 1);
this.lastPosition = new Vector3f(0, 0, 0);
this.lastRotation = new Vector3f(0, 0, 0);
this.lastScale = new Vector3f(1, 1, 1);
parentMatrix = new Matrix4f().identity();
}
public boolean requireTMUpdate() { //checks if the matrix has changed and requires an update
if(parent != null) {
return parent.requireTMUpdate();
}
if(!position.equals(lastPosition)) {
lastPosition.set(position);
return true;
}
if(!rotation.equals(lastRotation)) {
lastRotation.set(rotation);
return true;
}
if(!scale.equals(lastScale)) {
lastScale.set(scale);
return true;
}
return false;
}
public Matrix4f getTransformation() {
if((parent != null) && (requireTMUpdate())) {
if(!(getParent().equals(getParent().getParent()))) {
parentMatrix.set(parent.getTransformation());
//The above line sets the updated parentMatrix
setPosition(parentMatrix.transformPosition(position));
//The above line sets the position to where child is supposed to be in
//relation the parent, otherwise once the key is stopped being pressed,
//it will return to the position at the start of the game / program.
}
}else {
parentMatrix.rotationXYZ(0, 0, 0).translation(0, 0, 0).scale(1);
// The above line is supposed to reset the matrix, otherwise the previous
//transformations or rotations add up each frame and the it just gets messed
//up.
}
//System.out.println(parentMatrix.toString());
return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale))
// The transformationMatrix() method above from the Matrices class is
//supposed to return a worldMatrix.
}
public Vector3f getPosition() {
return position;
}
public void setPosition(Vector3f position) {
this.position = position;
}
public Vector3f getRotation() {
return rotation;
}
public void setRotation(Vector3f rotation) {
this.rotation = rotation;
}
public Vector3f getScale() {
return scale;
}
public void setScale(Vector3f scale) {
this.scale = scale;
}
public Transform getParent() {
return parent;
}
public void setParent(Transform parent) {
this.parent = parent;
}
}
Matrices class:
public class Matrices {
public static Matrix4f transformationMatrix(Vector3f pos, Vector3f rot, Vector3f scale) {
Matrix4f result = new Matrix4f();
result.identity();
result.translate(pos.x, pos.y, pos.z);
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));
/*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
result.rotate(rotation);
result.scale(scale.x, scale.y, scale.z);
return result;
}
public static Matrix4f transformationMatrix(Vector3f pos, Quaternionf rotation, Vector3f scale) {
Matrix4f result = new Matrix4f();
result.identity();
result.translate(pos.x, pos.y, pos.z);
/*Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));*/
/*result.rotate((float) Math.toRadians(rot.getX()), 1, 0, 0);
result.rotate((float) Math.toRadians(rot.getY()), 0, 1, 0);
result.rotate((float) Math.toRadians(rot.getZ()), 0, 0, 1);*/
result.rotate(rotation);
result.scale(scale.x, scale.y, scale.z);
return result;
}
public static Matrix4f viewMatrix(Vector3f pos, Vector3f rot) {
Matrix4f result = new Matrix4f();
result.identity();
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(rot.x)).
rotateY((float) Math.toRadians(rot.y)).
rotateZ((float) Math.toRadians(rot.z));
/*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
result.rotate(rotation);
result.translate(-pos.x, -pos.y, -pos.z);
return result;
}
public static Matrix4f viewMatrix(Vector3f pos, float pitch, float yaw, float roll) {
Matrix4f result = new Matrix4f();
result.identity();
Quaternionf rotation =
new Quaternionf().
identity().
rotateX((float) Math.toRadians(pitch)).
rotateY((float) Math.toRadians(yaw)).
rotateZ((float) Math.toRadians(roll));
/*result.rotate((float) Math.toRadians(rot.getX()), new org.joml.Vector3f(1, 0, 0));
result.rotate((float) Math.toRadians(rot.getY()), new org.joml.Vector3f(0, 1, 0));*/
result.rotate(rotation);
result.translate(-pos.x, -pos.y, -pos.z);
return result;
}
public static Matrix4f projectionMatrix(float FOV, float aspectRatio,
float NearPlaneDis, float FarPlaneDis) {
Matrix4f result = new Matrix4f();
result.identity();
result.perspective(FOV, aspectRatio, NearPlaneDis, FarPlaneDis);
/*float y_scale = (float) ((1f / Math.tan(Math.toRadians(FOV / 2f))) * aspectRatio);
float x_scale = y_scale / aspectRatio;
float frustum_length = FarPlaneDis - NearPlaneDis;
result.m00(x_scale);
result.m11(y_scale);
result.m22(-((FarPlaneDis + NearPlaneDis) / frustum_length));
result.m23(-1);
result.m32(-((2 * NearPlaneDis * FarPlaneDis) / frustum_length));
result.m33(0);*/
return result;
}
public static Matrix4f translate(Vector3f vec, Matrix4f src, Matrix4f dest) {
if (dest == null)
dest = new Matrix4f();
dest.m30(dest.m30() + src.m00() * vec.x + src.m10() * vec.y + src.m20() * vec.z);
dest.m31(dest.m31() + src.m01() * vec.x + src.m11() * vec.y + src.m21() * vec.z);
dest.m32(dest.m32() + src.m02() * vec.x + src.m12() * vec.y + src.m22() * vec.z);
dest.m33(dest.m33() + src.m03() * vec.x + src.m13() * vec.y + src.m23() * vec.z);
return dest;
}
public static Vector4f transform(Matrix4f left, Vector4f right, Vector4f dest) {
if (dest == null)
dest = new Vector4f(0, 0, 0, 0);
float x = left.m00() * right.x + left.m10() * right.y + left.m20() * right.z + left.m30() * right.w;
float y = left.m01() * right.x + left.m11() * right.y + left.m21() * right.z + left.m31() * right.w;
float z = left.m02() * right.x + left.m12() * right.y + left.m22() * right.z + left.m32() * right.w;
float w = left.m03() * right.x + left.m13() * right.y + left.m23() * right.z + left.m33() * right.w;
dest.x = x;
dest.y = y;
dest.z = z;
dest.w = w;
return dest;
}
public static Vector3f scale(Vector3f vector, float scale) {
vector.x *= scale;
vector.y *= scale;
vector.z *= scale;
return vector;
}
public static float[] getAll(Matrix4f matrix) {
float[] f = new float[16];
return matrix.get(f);
}
public static float barryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}
}
Finally, the Renderer class, where the objects are initialized:
public class Renderer {
private Model model;
private Model model2;
private GameObject root;
private GameObject child;
public Camera camera;
private float time = 0;
/*private float vertices[] = {
0.5f, 0.5f, 0f, //0 - top right
0.5f, -0.5f, 0f, //1 - bottom right
-0.5f, -0.5f, 0f, //2 - bottom left
-0.5f, 0.5f, 0f //3 - top left
};
private int indices[] = {
0, 1, 3, // first triangle
3, 1, 2 // second triangle
};*/
public Renderer() {
model = new Model("/backpack.obj");
model2 = new Model("/backpack.obj");
camera = new Camera(new Vector3f(0, 0, 0), new Vector3f(0, 0, 0));
root = new GameObject();
child = new GameObject();
root.addComponent(camera);
root.addComponent(model);
child.addComponent(model2);
root.addChild(child);
model.getTransform().setPosition(new Vector3f(0, 0, 0));
model2.getTransform().setPosition(new Vector3f(10, 0, 10));
}
public void render(Shader shader) {
if(Input.isKeyDown(GLFW.GLFW_KEY_RIGHT)) {
model.getTransform().setRotation(new Vector3f(0, time += 1f, 0));
}
//model2.getTransform().setRotation(new Vector3f(0, time += 0.01f, 0));
shader.bind();
root.input();
root.update();
root.render(shader, camera);
shader.unbind();
}
public void cleanUp() {
for(Mesh mesh: model.getMeshes()) {
mesh.cleanUp();
}
}
}
What exactly am I doing wrong here? Any help is appreciated!
Nvm, I found the solution after a day's worth of debugging:
This line:
return parentMatrix.mul(Matrices.transformationMatrix(position, rotation, scale));
should actually be:
return new Matrix4f(parentMatrix).mul(Matrices.transformationMatrix(position, rotation, scale));.
Turns out the mul method in the JOML Matrix4f class does not return a new Matrix4f() class of the worldMatrix that should be outputted instead multiplies it to the current matrix (parentMatrix here) itself, creating a completely different parentMatrix for the next frame.

Android OpenGL ES2 Skeletal animation child bone matrix

If I have a geometry with single bone I can render it with animation but if it has 2 bones the vertices do not animated correctly, I load a JSON format exported from blender, as I understand the child bone relative matrix multiplied by its parent absolute matrix then multiplied by it relative matrix inverse then each bone matrix multiplied by the key frame matrix to get the final matrix then send these matrices to the vertex shader.
I set the weight and bone index as attributes with the VBO.
for (int y = 0; y < g.getBones().size(); y++) {
com.thatulborooj.opengl.objects.Bone bone = new com.thatulborooj.opengl.objects.Bone();
Bone gBone = g.getBones().get(y);
float[] t = new float[] { gBone.getPosition().get(0), gBone.getPosition().get(0), gBone.getPosition().get(0) };
float[] r = new float[] { gBone.getRotation().get(0), gBone.getRotation().get(1), gBone.getRotation().get(2), gBone.getRotation().get(3) };
float[] s = gBone.getScale() == null ? new float[] { 1, 1, 1 } : new float[] { gBone.getScale().get(0), gBone.getScale().get(1), gBone.getScale().get(2) };
float[] nMatrix = createMat4(t, r, s);
float[] inverseMatrix = new float[16];
if (gBone.getParent() == -1) {
bone.setBindMatrix(nMatrix);
invertM(inverseMatrix, 0, nMatrix, 0);
float[] fMatrix = new float[16];
multiplyMM(fMatrix, 0, nMatrix, 0, inverseMatrix, 0);
bone.setFinalMatrix(fMatrix);
} else {
float[] pMatrix = mesh.getBones().get(gBone.getParent()).getBindMatrix();
float[] bMatrix = new float[16];
multiplyMM(bMatrix, 0, pMatrix, 0, nMatrix, 0);
bone.setBindMatrix(bMatrix);
invertM(inverseMatrix, 0, bMatrix, 0);
float[] fMatrix = new float[16];
multiplyMM(fMatrix, 0, bMatrix, 0, inverseMatrix, 0);
bone.setFinalMatrix(fMatrix);
}
bone.setName(gBone.getName());
bone.setParent(gBone.getParent());
mesh.getBones().add(bone);
}
}
this is the render code
if (animated) {
if (playing) {
if (curFrame < frames) {
for (int i = 0; i < bones.size(); i++) {
Key key = animations.get(0).getHierarchy().get(i).getKeys().get(curFrame);
setIdentityM(aMatrix, 0);
multiplyMM(aMatrix, 0, bones.get(i).getFinalMatrix(), 0, key.getMatrix(), 0);
for (int q = 0; q < 16; q++) {
bs[i][q] = aMatrix[q];
}
}
curFrame++;
} else
curFrame = 0;
shaderProgram.setBones(bs);
}
}
this is the function which make the matrix from position, quaternion and scale:
public static float[] createMat4(float[] t, float[] r, float[] s) {
float[] mat4 = new float[16];
float[] T = new float[16];
float[] R = quaternionToMatrix(r);
float[] S = new float[16];
setIdentityM(T, 0);
setIdentityM(S, 0);
translateM(T, 0, t[0], t[1], t[2]);
scaleM(S, 0, s[0], s[1], s[2]);
float[] temp = new float[16];
multiplyMM(temp, 0, T, 0, R, 0);
multiplyMM(mat4, 0, temp, 0, S, 0);
return mat4;
}
private static float[] quaternionToMatrix(float[] q) {
float[] m = new float[16];
final float xx = q[0] * q[0];
final float xy = q[0] * q[1];
final float xz = q[0] * q[2];
final float xw = q[0] * q[3];
final float yy = q[1] * q[1];
final float yz = q[1] * q[2];
final float yw = q[1] * q[3];
final float zz = q[2] * q[2];
final float zw = q[2] * q[3];
// Set matrix from quaternion
m[0] = 1 - 2 * (yy + zz);
m[1] = 2 * (xy - zw);
m[2] = 2 * (xz + yw);
m[3] = 0;
m[4] = 2 * (xy + zw);
m[5] = 1 - 2 * (xx + zz);
m[6] = 2 * (yz - xw);
m[7] = 0;
m[8] = 2 * (xz - yw);
m[9] = 2 * (yz + xw);
m[10] = 1 - 2 * (xx + yy);
m[11] = 0;
m[12] = 0;
m[13] = 0;
m[14] = 0;
m[15] = 1;
return m;
}
I set the bones array uniform by this line:
private void setBonesUniforms() {
for (int i = 0; i < bones.length; i++) {
int uBonesLocation = glGetUniformLocation(program, "bones[" + i + "]");
glUniformMatrix4fv(uBonesLocation, 1, false, bones[i], 0);
}
}
finally this is the vertex shader:
vec4 newVertex=vertexPosition;
vec4 newNormal=vertexNormal;
if(animated==1){
int index;
index=int(skinindex.x);
newVertex = (bones[index] * skinweight.x) * vertexPosition;
newNormal = (bones[index] * skinweight.x) * vertexNormal;
index=int(skinindex.y);
newVertex += (bones[index] * skinweight.y) * vertexPosition;
newNormal += (bones[index] * skinweight.y) * vertexNormal;
}
newVertex=vec4(newVertex.xyz, 1.0);
I noticed that changing the child bone matrix with any floats do not change the result.
You can use AssimpLib it is easy to use and supports most formats including collada with skeletal animations

How to apply color to an irregular polygon with unknown indices?

I'm having a set of vertices (X,Y) of an irregular polygon, some having 4 vertices and some more than 4. I'm creating an array of vertices for drawing from the set of vertices I have using the getTransformedVertices() method. The indices and UV for the texture are unknown so I had to calculate tem using Triangulate() method below. I've setup the color and texture in the setColor() and setImage() methods.
However, the polygons having more than 4 vertices are not rendered properly. I've been trying for weeks now and almost the searched half of the internet. This is what I could come up with. The polygons with 4 vertices and the outlines are working properly. But I couldn't get the textures displayed properly on the polygon. Please help
public class BoothRectangle
{
float angle;
float scale;
RectF base;
PointF translation;
int textureId;
int positionBufferId;
int textureBufferId;
PointF[] corners;
float[] verts;
// Geometric variables
public float vertices[];
public float colors[];
public short indices[];
public float uvs[];
public FloatBuffer vertexBuffer;
public ShortBuffer drawListBuffer;
public FloatBuffer colorBuffer;
public FloatBuffer uvBuffer;
TextPaint textPaint = new TextPaint();
String title;
public BoothRectangle(PointF[] corners, int textureId, float[] colors, String title)
{
// Initialise our intital size around the 0,0 point
base = new RectF(corners[1].x, corners[3].y, corners[0].x, corners[1].y);
this.corners = corners;
this.title = title;
// Offset translation
translation = new PointF(0f,0f);
// Initial Size
scale = 1f;
// We start in our inital angle
angle = 0f;
this.textureId = textureId;
this.colors = colors;
}
public void setColor(float[] topColor){
List<Float> colorsList = new ArrayList<Float>();
for(PointF point : corners){
colorsList.add(topColor[0] / 255);
colorsList.add(topColor[1] / 255);
colorsList.add(topColor[2] / 255);
colorsList.add(1f);
}
int i = 0;
float[] colors = new float[colorsList.size()];
for (Float f : colorsList) {
colors[i++] = (f != null ? f : Float.NaN);
}
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
cbb.order(ByteOrder.nativeOrder());
colorBuffer = cbb.asFloatBuffer();
colorBuffer.put(colors);
colorBuffer.position(0);
int[] buffers = new int[1];
GLES11.glGenBuffers(1, buffers, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, buffers[0]);
GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, 4 * colors.length, colorBuffer, GLES11.GL_STATIC_DRAW);
textureBufferId = buffers[0];
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
}
public float[] getTransformedVertices()
{
float z;
List<Float> finalVertices = new ArrayList<Float>();
if(textureId == 0)
z = 0.5f;
else
z = 2.0f;
finalVertices.clear();
for(PointF point : corners){
finalVertices.add(point.x);
finalVertices.add(point.y);
finalVertices.add(0.0f);
}
int i = 0;
float[] verticesArray = new float[finalVertices.size()];
for (Float f : finalVertices) {
verticesArray[i++] = (f != null ? f : Float.NaN);
}
return verticesArray;
}
public void setImage()
{
uvs = new float[] {
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
// The texture buffer
ByteBuffer bb = ByteBuffer.allocateDirect(uvs.length * 4);
bb.order(ByteOrder.nativeOrder());
uvBuffer = bb.asFloatBuffer();
uvBuffer.put(uvs);
uvBuffer.position(0);
int[] buffers = new int[1];
GLES11.glGenBuffers(1, buffers, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, buffers[0]);
GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, 4 * uvs.length, uvBuffer, GLES11.GL_STATIC_DRAW);
textureBufferId = buffers[0];
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
}
public void initBooth()
{
vertices = this.getTransformedVertices();
indices = PolygonTriangulation.Process(vertices);
if(indices==null){
Log.d("PolygonTriangulation",title + " - failed");
if(this.corners.length == 4){
indices = new short[] {2, 1, 0, 2, 0, 3};
}else{
indices = new short[corners.length];
for(int i=0;i<corners.length;i++){
indices[i] = (short) i;
}
}
}
// The vertex buffer.
ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(indices);
drawListBuffer.position(0);
int[] buffers = new int[1];
GLES11.glGenBuffers(1, buffers, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, buffers[0]);
GLES11.glBufferData(GLES11.GL_ARRAY_BUFFER, 4 * vertices.length, vertexBuffer, GLES11.GL_STATIC_DRAW);
positionBufferId = buffers[0];
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
if(this.textureId !=0){
setImage();
} else {
setColor(colors);
}
}
public void Render(GL10 gl){
if(textureId == 0){
GLES11.glPushMatrix();
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, positionBufferId);
GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glFrontFace(GL10.GL_CW);
GLES11.glColor4f(0.8f, 0.8f, 0.8f, 0.8f);
GLES11.glLineWidth(3.0f);
GLES11.glDrawArrays(GL10.GL_LINE_LOOP, 0, corners.length);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, textureBufferId);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
GLES11.glColorPointer(4, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glDrawElements(GL10.GL_TRIANGLES, indices.length,
GL10.GL_UNSIGNED_SHORT, drawListBuffer);
GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glDisableClientState(GL10.GL_COLOR_ARRAY);
GLES11.glPopMatrix();
} else {
GLES11.glPushMatrix();
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, positionBufferId);
GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, textureBufferId);
GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, 0);
GLES11.glBindBuffer(GLES11.GL_ARRAY_BUFFER, 0);
GLES11.glEnable(GL10.GL_TEXTURE_2D);
GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
GLES11.glEnable(GL10.GL_BLEND);
GLES11.glFrontFace(GL10.GL_CW);
GLES11.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
int error = gl.glGetError();
if (error != GL10.GL_NO_ERROR)
{
Log.e("OPENGL", "GL Texture Load Error: " + GLU.gluErrorString(error));
}
GLES11.glDrawElements(GL10.GL_TRIANGLES, indices.length,
GL10.GL_UNSIGNED_SHORT, drawListBuffer);
GLES11.glDisable(GL10.GL_BLEND);
GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
GLES11.glDisable(GL10.GL_TEXTURE_2D);
GLES11.glPopMatrix();
}
}
}
PolygonTriangulation.java - got from AssetFX - http://www.experts-exchange.com/Programming/Languages/Java/Q_27882746.html
public class PolygonTriangulation {
static final float EPSILON=0.0000000001f;
static public float Area(float[] contour) {
int n = contour.length;
float A = 0.0f;
for(int p = n - 3, q = 0; q < n; p=q, q+=3)
{
A += contour[p] * contour[q+1] - contour[q] * contour[p+1];
}
return A * 0.5f;
}
static public boolean InsideTriangle(float Ax,float Ay,float Bx,float By,float Cx,float Cy,float Px,float Py){
float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy;
float cCROSSap, bCROSScp, aCROSSbp;
ax = Cx - Bx; ay = Cy - By;
bx = Ax - Cx; by = Ay - Cy;
cx = Bx - Ax; cy = By - Ay;
apx= Px - Ax; apy= Py - Ay;
bpx= Px - Bx; bpy= Py - By;
cpx= Px - Cx; cpy= Py - Cy;
aCROSSbp = ax * bpy - ay * bpx;
cCROSSap = cx * apy - cy * apx;
bCROSScp = bx * cpy - by * cpx;
return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f));
}
static public boolean Snip(float[] contour, int u, int v, int w, int n, int[] V) {
int p;
float Ax, Ay, Bx, By, Cx, Cy, Px, Py;
Ax = contour[V[u]];
Ay = contour[V[u+1]];
Bx = contour[V[v]];
By = contour[V[v+1]];
Cx = contour[V[w]];
Cy = contour[V[w+1]];
if ( EPSILON > (((Bx-Ax)*(Cy-Ay)) - ((By-Ay)*(Cx-Ax))) ){
return false;
}
for (p = 0; p < n; p++)
{
if( (p == u/2) || (p == v/2) || (p == w/2) ){
continue;
}
Px = contour[V[p*2]];
Py = contour[V[(p*2)+1]];
if (InsideTriangle(Ax,Ay,Bx,By,Cx,Cy,Px,Py)){
return false;
}
}
return true;
}
//Brings in 3D vertex float array but processes it as 2D only.
static public short[] Process(float[] contour) {
//Pre-return list
ArrayList<Integer> vertexArray = new ArrayList<Integer>();
/* allocate and initialize list of Vertices in polygon */
int n = contour.length;
if ( n/3 < 3 )
return null;
//The (n/3)*2 occurs as we are removing the z coordinate from the mix
int[] V = new int[(n/3)*2];
/* we want a counter-clockwise polygon in V */
if (0.0f < Area(contour)){
for (int s = 0, v = 0; v < n-1; s+=2, v += 3){
V[s] = v;
V[s + 1] = v + 1;
}
}
else{
for(int s = 0, v = 0; v < n-1; s += 2, v += 3){
V[s] = (n - 1) - (v + 2);
V[s + 1] = (n - 1) - (v + 1);
}
}
int nv = n/3;
/* remove nv-2 Vertices, creating 1 triangle every time */
int count = 2 * nv; /* error detection */
for(int v = nv - 1; nv > 2;)
{
/* if we loop, it is probably a non-simple polygon */
if (0 >= (count--))
{
//** Triangulate: ERROR - probable bad polygon!
Log.e("PolygonTriangulation","Invalid Polygon");
return null;
}
/* three consecutive vertices in current polygon, <u,v,w> */
int u = v;
if (nv <= u)
u = 0; /* previous */
v = u + 1;
if (nv <= v)
v = 0; /* new v */
int w = v + 1;
if (nv <= w)
w = 0; /* next */
if (Snip(contour, u*2, v*2, w*2, nv, V))
{
vertexArray.add(V[u*2]/3);
vertexArray.add(V[v*2]/3);
vertexArray.add(V[w*2]/3);
// remove v from remaining polygon
for(int s = v * 2, t = (v * 2) + 2; t < (nv * 2); s += 2, t += 2){
V[s] = V[t];
V[s+1] = V[t+1];
}
nv--;
// reset error detection counter
count = 2 * nv;
}
}
//Convert ArrayList into short array
short[] index = new short[vertexArray.size()];
for(int i = 0; i < vertexArray.size(); i++){
index[i] = vertexArray.get(i).shortValue();
}
return index;
}
}

Poser Mesh with openGL 1.0, 50 % of Triangles seem to miss

I generated a 3D model with Poser to use it with openGL 1.0 in a Android Application. When I render the mesh I can see the 3D Model as generated in Poser 8, but unfortunately only half of the triangles are rendered - it look like I have to draw some kind of squares instead. Is there any method to mirror the existing triangles so I can draw the missing ones?
My mesh is generated from a wavefront object. Therefor I use a class called Mesh
public final class Mesh {
public enum PrimitiveType {
Points,
Lines,
Triangles,
LineStrip,
TriangleStrip,
TriangleFan
}
// gl instance
private GL10 gl;
// vertex position buffer, array
private float vertices[];
private int vertexHandle;
private FloatBuffer vertexBuffer;
// color buffer, array
private float colors[];
private int colorHandle;
private FloatBuffer colorBuffer;
// texture coordinate buffer, array
private float texCoords[];
private int texHandle;
private FloatBuffer texBuffer;
// normal (for illumination) buffer, array
private float normals[];
private int normalHandle;
private FloatBuffer normalBuffer;
// index where next vertices will be inserted
private int index = 0;
// number vertices for mesh
private int numVertices = 0;
// renderer support vbos
private boolean globalVBO = true;
// is mesh dirty
private boolean dirty = true;
// last mesh
private static Mesh lastMesh;
// mesh count
public static int numMeshes = 0;
/**
* after calling constructor first set attribute (color, texture, normal), then fix the vertex by calling vertex(...)
*
* #param gl GL10
* #param numVertices number vertices of mesh
* #param hasColors using colors?
* #param hasTextureCoordinates using textures coordinates
* #param hasNormals using normals?
*/
public Mesh(GL10 gl, int numVertices, boolean hasColors, boolean hasTextureCoordinates, boolean hasNormals) {
this.gl = gl;
vertices = new float[numVertices * 3];
int[] buffer = new int[1];
if (!globalVBO) {
vertexBuffer = allocateBuffer(numVertices * 3);
} else {
((GL11) gl).glGenBuffers(1, buffer, 0);
vertexHandle = buffer[0];
vertexBuffer = FloatBuffer.wrap(vertices);
}
if (hasColors) {
colors = new float[numVertices * 4];
if (!globalVBO) {
colorBuffer = allocateBuffer(numVertices * 3);
} else {
((GL11) gl).glGenBuffers(1, buffer, 0);
colorHandle = buffer[0];
colorBuffer = FloatBuffer.wrap(colors);
}
}
if (hasTextureCoordinates) {
texCoords = new float[numVertices * 2];
if (!globalVBO) {
texBuffer = allocateBuffer(numVertices * 3);
} else {
((GL11) gl).glGenBuffers(1, buffer, 0);
texHandle = buffer[0];
texBuffer = FloatBuffer.wrap(texCoords);
}
}
if (hasNormals) {
normals = new float[numVertices * 3];
if (!globalVBO) {
normalBuffer = allocateBuffer(numVertices * 3);
} else {
((GL11) gl).glGenBuffers(1, buffer, 0);
normalHandle = buffer[0];
normalBuffer = FloatBuffer.wrap(normals);
}
}
}
/**
* allocates FloatBuffer of size
*
* #param size size number of floats
* #return FloatBuffer
*/
private FloatBuffer allocateBuffer(int size) {
ByteBuffer buffer = ByteBuffer.allocateDirect(size * 4);
buffer.order(ByteOrder.nativeOrder());
return buffer.asFloatBuffer();
}
/**
* renders mesh given type, starts at offset wirh numVertices vertices
*
* #param type PrimitiveType (see above)
* #param offset offset in number of vertices
* #param numVertices number of vertices to use
*/
public void render(PrimitiveType type, int offset, int numVertices) {
boolean wasDirty = dirty;
if (dirty) {
update();
}
if (this == lastMesh && !wasDirty) {
gl.glDrawArrays(getPrimitiveType(type), offset, numVertices);
return;
} else {
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
}
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
if (globalVBO) {
((GL11) gl).glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexHandle);
((GL11) gl).glVertexPointer(3, GL10.GL_FLOAT, 0, 0);
} else {
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer);
}
if (colors != null) {
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
if (globalVBO) {
((GL11) gl).glBindBuffer(GL11.GL_ARRAY_BUFFER, colorHandle);
((GL11) gl).glColorPointer(4, GL10.GL_FLOAT, 0, 0);
} else
gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer);
}
if (texCoords != null) {
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
if (globalVBO) {
((GL11) gl).glBindBuffer(GL11.GL_ARRAY_BUFFER, texHandle);
((GL11) gl).glTexCoordPointer(2, GL10.GL_FLOAT, 0, 0);
} else
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, texBuffer);
}
if (normals != null) {
gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
if (globalVBO) {
((GL11) gl).glBindBuffer(GL11.GL_ARRAY_BUFFER, normalHandle);
((GL11) gl).glNormalPointer(GL10.GL_FLOAT, 0, 0);
} else
gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer);
}
gl.glDrawArrays(getPrimitiveType(type), offset, numVertices);
lastMesh = this;
}
/**
* renders mesh as given type with numVertices from calling vertex()
*
* #param type PrimitveType
*/
public void render(PrimitiveType type) {
render(type, 0, numVertices);
}
/**
* returns openGL constant of PrimitiveType
*
* #param type PrimitiveType (enum above)
* #return openGL constant
*/
private int getPrimitiveType(PrimitiveType type) {
if (type == PrimitiveType.Lines) {
return GL10.GL_LINES;
} else if (type == PrimitiveType.Triangles) {
return GL10.GL_TRIANGLES;
} else if (type == PrimitiveType.LineStrip) {
return GL10.GL_LINE_STRIP;
} else if (type == PrimitiveType.TriangleStrip) {
return GL10.GL_TRIANGLE_STRIP;
} else if (type == PrimitiveType.Points) {
return GL10.GL_POINTS;
} else {
return GL10.GL_TRIANGLE_FAN;
}
}
/**
* updates the direct buffers in case the user
*/
private void update() {
if (!globalVBO) {
vertexBuffer.put(vertices);
vertexBuffer.position(0);
if (colors != null) {
colorBuffer.put(colors);
colorBuffer.position(0);
}
if (texCoords != null) {
texBuffer.put(texCoords);
texBuffer.position(0);
}
if (normals != null) {
normalBuffer.put(normals);
normalBuffer.position(0);
}
} else {
GL11 gl = (GL11) this.gl;
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, vertexHandle);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, vertices.length * 4, vertexBuffer, GL11.GL_DYNAMIC_DRAW);
if (colors != null) {
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, colorHandle);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, colors.length * 4, colorBuffer, GL11.GL_DYNAMIC_DRAW);
}
if (normals != null) {
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, normalHandle);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, normals.length * 4, normalBuffer, GL11.GL_DYNAMIC_DRAW);
}
if (texCoords != null) {
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, texHandle);
gl.glBufferData(GL11.GL_ARRAY_BUFFER, texCoords.length * 4, texBuffer, GL11.GL_DYNAMIC_DRAW);
}
gl.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
}
numVertices = index;
index = 0;
dirty = false;
}
/**
* defines position of current vertex, before calling call method like color, normal or texCoord
*
* #param x x coordinate
* #param y y coordinate
* #param z z coordinate
*/
public void vertex(float x, float y, float z) {
dirty = true;
int offset = index * 3;
vertices[offset] = x;
vertices[offset + 1] = y;
vertices[offset + 2] = z;
index++;
}
/**
* sets color of current vertex
*
* #param r red
* #param g green
* #param b blue
* #param a alpha
*/
public void color(float r, float g, float b, float a) {
dirty = true;
int offset = index * 4;
colors[offset] = r;
colors[offset + 1] = g;
colors[offset + 2] = b;
colors[offset + 3] = a;
}
/**
* sets the normal of current vertex
*
* #param x x components
* #param y y components
* #param z z components
*/
public void normal(float x, float y, float z) {
dirty = true;
int offset = index * 3;
normals[offset] = x;
normals[offset + 1] = y;
normals[offset + 2] = z;
}
/**
* sets texture coordinates of current vertex
*
* #param s s coordinate (correlates x)
* #param t t coordinate (correlates y)
*/
public void texCoord(float s, float t) {
dirty = true;
int offset = index * 2;
texCoords[offset] = s;
texCoords[offset + 1] = t;
}
/**
* deletes all buffers, sets all attributes to null
*/
public void dispose() {
if (globalVBO) {
GL11 gl = (GL11) this.gl;
if (vertexHandle != -1)
gl.glDeleteBuffers(1, new int[]{vertexHandle}, 0);
if (colorHandle != -1)
gl.glDeleteBuffers(1, new int[]{colorHandle}, 0);
if (normalHandle != -1)
gl.glDeleteBuffers(1, new int[]{normalHandle}, 0);
if (texHandle != -1)
gl.glDeleteBuffers(1, new int[]{texHandle}, 0);
}
vertices = null;
vertexBuffer = null;
colors = null;
colorBuffer = null;
normals = null;
normalBuffer = null;
texCoords = null;
texBuffer = null;
numMeshes--;
}
/**
* #return number of vertices
*/
public int getMaximumVertices() {
return vertices.length / 3;
}
/**
* resets index
*/
public void reset() {
dirty = true;
index = 0;
}
}
and a class called MeshLoader
public class MeshLoader {
/**
*
* Loads a mesh from the given InputStream
* #param gl GL10 instance
* #return The mesh
*/
public static Mesh loadObj( GL10 gl, InputStream in )
{
String line = "";
try
{
BufferedReader reader = new BufferedReader( new InputStreamReader(in) );
StringBuffer b = new StringBuffer();
String l = reader.readLine();
while( l != null )
{
b.append( l );
b.append( "\n" );
l = reader.readLine();
}
line = b.toString();
reader.close();
}
catch( Exception ex )
{
throw new RuntimeException(ex.getMessage() + " " +
"couldn't load file mesh from input stream" );
}
return loadObjFromString( gl, line );
}
/**
* Loads a mesh from the given string in obj format
*
* #param obj The string
* #return The Mesh
*/
public static Mesh loadObjFromString( GL10 gl, String obj )
{
String[] lines = obj.split( "\n" );
float[] vertices = new float[lines.length * 3];
float[] normals = new float[lines.length * 3];
float[] uv = new float[lines.length * 3];
int numVertices = 0;
int numNormals = 0;
int numUV = 0;
int numFaces = 0;
int[] facesVerts = new int[lines.length * 3];
int[] facesNormals = new int[lines.length * 3];
int[] facesUV = new int[lines.length * 3];
int vertexIndex = 0;
int normalIndex = 0;
int uvIndex = 0;
int faceIndex = 0;
for( int i = 0; i < lines.length; i++ )
{
String line = lines[i];
if( line.startsWith( "v " ) )
{
String[] tokens = line.split( " " );
vertices[vertexIndex] = Float.parseFloat(tokens[1]);
vertices[vertexIndex+1] = Float.parseFloat(tokens[2]);
vertices[vertexIndex+2] = Float.parseFloat(tokens[3]);
vertexIndex += 3;
numVertices++;
continue;
}
if( line.startsWith( "vn " ) )
{
String[] tokens = line.split( " " );
normals[normalIndex] = Float.parseFloat(tokens[1]);
normals[normalIndex+1] = Float.parseFloat(tokens[2]);
normals[normalIndex+2] = Float.parseFloat(tokens[3]);
normalIndex += 3;
numNormals++;
continue;
}
// coords of each texture point
if( line.startsWith( "vt" ) )
{
String[] tokens = line.split( " " );
uv[uvIndex] = Float.parseFloat(tokens[1]);
uv[uvIndex+1] = Float.parseFloat(tokens[2]);
uvIndex += 2;
numUV++;
continue;
}
if( line.startsWith( "f " ) )
{
String[] tokens = line.split( " " );
String[] parts = tokens[1].split("/");
facesVerts[faceIndex] = getIndex(parts[0], numVertices);
facesNormals[faceIndex] = getIndex(parts[2], numNormals);
facesUV[faceIndex] = getIndex(parts[1], numUV);
faceIndex++;
parts = tokens[2].split("/");
facesVerts[faceIndex] = getIndex(parts[0], numVertices);
facesNormals[faceIndex] = getIndex(parts[2], numNormals);
facesUV[faceIndex] = getIndex(parts[1], numUV);
faceIndex++;
parts = tokens[3].split("/");
facesVerts[faceIndex] = getIndex(parts[0], numVertices);
facesNormals[faceIndex] = getIndex(parts[2], numNormals);
facesUV[faceIndex] = getIndex(parts[1], numUV);
faceIndex++;
numFaces++;
continue;
}
}
Mesh mesh = new Mesh(gl, numFaces * 3, false ,numUV > 0, numNormals > 0 );
for( int i = 0; i < numFaces*3; i++ )
{
if( numNormals > 0 )
{
int normalIdx = facesNormals[i] * 3;
mesh.normal( normals[normalIdx], normals[normalIdx+1], normals[normalIdx+2] );
}
if( numUV > 0 )
{
int uvIdx = facesUV[i] * 2;
mesh.texCoord( uv[uvIdx], uv[uvIdx+1]);
}
int vertexIdx = facesVerts[i] *3;
mesh.vertex( vertices[vertexIdx], vertices[vertexIdx+1], vertices[vertexIdx+2] );
}
return mesh;
}
private static int getIndex( String index, int size )
{
if( index == null || index.length() == 0 )
return 0;
int idx = Integer.parseInt( index );
if( idx < 0 )
return size + idx;
else
return idx - 1;
}
}
every 3D Object is a Mesh and will be generated by the MeshLoader.loadObject Method.
This works perfectly with simple Objects. But not with 3D Models generated by Poser 8.
Does anyone have an idea, how to solve this problem?
I solved it.
I triangulated the Mesh and reduced the number of vertices under 32.000. Therefor I exported the Mesh from Poser as Collada-File and imported it to Blender (because there are more tutorials :D).

Drawing a sphere in OpenGL ES 2.0

I'm trying to draw a sphere in openGL ES 2.0 on Android. I already looked at the related questions and tried some of their code but I still can't get it to work.
Based on the Android developer examples and this code found on gamedev.net I came up with the code below. However it is not drawing correctly; When using glDrawArrays() rendering works but the results are not correct, when using glDrawElements() I get an GL_INVALID_OPERATION error. I listed the contents of my buffers below.
Sphere.java
public class Sphere
{
private int stacks;
private int slices;
private float radius;
//Buffers
private FloatBuffer vertexBuffer;
private FloatBuffer colorBuffer;
private ShortBuffer indexBuffer;
//Buffer sizes in aantal bytes
private int vertexBufferSize;
private int colorBufferSize;
private int indexBufferSize;
private int vertexCount;
private int program;
static final int FLOATS_PER_VERTEX = 3; // Het aantal floats in een vertex (x, y, z)
static final int FLOATS_PER_COLOR = 4; // Het aantal floats in een kleur (r, g, b, a)
static final int SHORTS_PER_INDEX = 2;
static final int BYTES_PER_FLOAT = 4;
static final int BYTES_PER_SHORT = 2;
static final int BYTES_PER_VERTEX = FLOATS_PER_VERTEX * BYTES_PER_FLOAT;
static final int BYTES_PER_COLOR = FLOATS_PER_COLOR * BYTES_PER_FLOAT;
static final int BYTES_PER_INDEX_ENTRY = SHORTS_PER_INDEX * BYTES_PER_SHORT;
// Set color with red, green, blue and alpha (opacity) values
private float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
public Sphere(float radius, int stacks, int slices)
{
this.stacks = stacks;
this.slices = slices;
this.radius = radius;
vertexCount = (stacks+1) * (slices+1);
vertexBufferSize = vertexCount * BYTES_PER_VERTEX;
colorBufferSize = vertexCount * BYTES_PER_COLOR;
indexBufferSize = vertexCount * BYTES_PER_INDEX_ENTRY;
program = GLHelpers.createProgram();
if (program == 0) {
return;
}
GLHelpers.checkGlError("program");
// Setup vertex-array buffer. Vertices in float. A float has 4 bytes.
vertexBuffer = ByteBuffer.allocateDirect(vertexBufferSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
colorBuffer = ByteBuffer.allocateDirect(colorBufferSize).order(ByteOrder.nativeOrder()).asFloatBuffer();
indexBuffer = ByteBuffer.allocateDirect(indexBufferSize).order(ByteOrder.nativeOrder()).asShortBuffer();
generateSphereCoords(radius, stacks, slices);
vertexBuffer.position(0);
colorBuffer.position(0);
indexBuffer.position(0);
}
public void draw(float[] modelViewProjectionMatrix)
{
GLES20.glUseProgram(program);
GLHelpers.checkGlError("useprogram");
int positionHandle = GLES20.glGetAttribLocation(program, "a_Position");
GLES20.glEnableVertexAttribArray(positionHandle);
GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, BYTES_PER_VERTEX, vertexBuffer);
GLHelpers.checkGlError("pos");
//int colorHandle = GLES20.glGetAttribLocation(program, "a_Color");
//GLES20.glEnableVertexAttribArray(colorHandle);
//GLES20.glVertexAttribPointer(colorHandle, 4, GLES20.GL_FLOAT, false, BYTES_PER_COLOR, colorBuffer);
//GLHelpers.checkGlError("color");
int matrixHandle = GLES20.glGetUniformLocation(program, "u_Matrix");
GLES20.glUniformMatrix4fv(matrixHandle, 1, false, modelViewProjectionMatrix, 0);
/*
* When using glDrawArrays rendering works but the results are not correct, when using glDrawElements I get an GL_INVALID_OPERATION error.
*/
GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexBuffer.capacity(), GLES20.GL_SHORT, indexBuffer);
//GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
GLHelpers.checkGlError("draw");
// Disable vertex array
GLES20.glDisableVertexAttribArray(positionHandle);
//GLES20.glDisableVertexAttribArray(colorHandle);
}
private void generateSphereCoords(float radius, int stacks, int slices)
{
for (int stackNumber = 0; stackNumber <= stacks; ++stackNumber)
{
for (int sliceNumber = 0; sliceNumber < slices; ++sliceNumber)
{
float theta = (float) (stackNumber * Math.PI / stacks);
float phi = (float) (sliceNumber * 2 * Math.PI / slices);
float sinTheta = FloatMath.sin(theta);
float sinPhi = FloatMath.sin(phi);
float cosTheta = FloatMath.cos(theta);
float cosPhi = FloatMath.cos(phi);
vertexBuffer.put(new float[]{radius * cosPhi * sinTheta, radius * sinPhi * sinTheta, radius * cosTheta});
}
}
for (int stackNumber = 0; stackNumber < stacks; ++stackNumber)
{
for (int sliceNumber = 0; sliceNumber <= slices; ++sliceNumber)
{
indexBuffer.put((short) ((stackNumber * slices) + (sliceNumber % slices)));
indexBuffer.put((short) (((stackNumber + 1) * slices) + (sliceNumber % slices)));
}
}
}
}
GLHelpers.java
public class GLHelpers
{
private static final String TAG = "GLHelpers";
private static final String VERTEX_SHADER_CODE =
"uniform mat4 u_Matrix;" +
"attribute vec4 a_Position;" +
"attribute vec4 a_Color;" +
"varying vec4 v_Color;" +
"void main() {" +
" v_Color = a_Color;" +
" gl_Position = a_Position * u_Matrix;" +
"}";
private static final String FRAGMENT_SHADER_CODE =
"precision mediump float;" +
"varying vec4 v_Color;" +
"void main() {" +
" gl_FragColor = v_Color;" +
"}";
private static int loadShader(int shaderType, String source)
{
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0)
{
Log.e(TAG, "Could not compile shader " + shaderType + ":");
Log.e(TAG, GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
public static int createProgram()
{
int vertexShader = GLHelpers.loadShader(GLES20.GL_VERTEX_SHADER, GLHelpers.VERTEX_SHADER_CODE);
if (vertexShader == 0)
return 0;
int pixelShader = GLHelpers.loadShader(GLES20.GL_FRAGMENT_SHADER, GLHelpers.FRAGMENT_SHADER_CODE);
if (pixelShader == 0)
return 0;
int program = GLES20.glCreateProgram();
if (program != 0) {
GLES20.glAttachShader(program, vertexShader);
GLHelpers.checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
GLHelpers.checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE)
{
Log.e(TAG, "Could not link program: ");
Log.e(TAG, GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
public static void checkGlError(String glOperation)
{
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR)
{
Log.e(TAG, glOperation + ": glError " + error);
throw new RuntimeException(glOperation + ": glError " + error);
}
}
}
The contents of vertexBuffer
X Y Z
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
-0.0, 0.0, 1.0,
-0.0, -0.0, 1.0,
0.0, -0.0, 1.0,
0.58778524, 0.0, 0.809017,
0.18163562, 0.559017, 0.809017,
-0.4755283, 0.34549147, 0.809017,
-0.4755282, -0.34549156, 0.809017,
0.18163571, -0.55901694, 0.809017,
0.95105654, 0.0, 0.30901697,
0.29389262, 0.90450853, 0.30901697,
-0.769421, 0.55901694, 0.30901697,
-0.76942086, -0.5590171, 0.30901697,
0.29389274, -0.9045085, 0.30901697,
0.9510565, 0.0, -0.30901703,
0.2938926, 0.9045085, -0.30901703,
-0.7694209, 0.5590169, -0.30901703,
-0.7694208, -0.55901706, -0.30901703,
0.29389274, -0.9045084, -0.30901703,
0.5877852, 0.0, -0.80901706,
0.1816356, 0.55901694, -0.80901706,
-0.47552824, 0.3454914, -0.80901706,
-0.47552818, -0.34549153, -0.80901706,
0.1816357, -0.5590169, -0.80901706,
-8.742278E-8, -0.0, -1.0,
-2.7015123E-8, -8.3144E-8, -1.0,
7.0726514E-8, -5.138581E-8, -1.0,
7.072651E-8, 5.138583E-8, -1.0,
-2.7015135E-8, 8.3143995E-8, -1.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0,
0.0, 0.0, 0.0
The contents of indexBuffer
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
The result when using glDrawArrays():
As promised the working code for creating the sphere:
public static Model3D createSphere(float radius, int stacks, int slices)
{
int vertexCount = (stacks + 1) * (slices + 1);
FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_VERTEX).order(ByteOrder.nativeOrder()).asFloatBuffer();
FloatBuffer normalBuffer = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_NORMAL).order(ByteOrder.nativeOrder()).asFloatBuffer();
FloatBuffer textureCoordBuffer = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_TEXTURE_COORD).order(ByteOrder.nativeOrder()).asFloatBuffer();
ShortBuffer indexBuffer = ByteBuffer.allocateDirect(vertexCount * GLHelpers.BYTES_PER_TRIANGLE_INDEX).order(ByteOrder.nativeOrder()).asShortBuffer();
for (int stackNumber = 0; stackNumber <= stacks; ++stackNumber)
{
for (int sliceNumber = 0; sliceNumber <= slices; ++sliceNumber)
{
float theta = (float) (stackNumber * Math.PI / stacks);
float phi = (float) (sliceNumber * 2 * Math.PI / slices);
float sinTheta = FloatMath.sin(theta);
float sinPhi = FloatMath.sin(phi);
float cosTheta = FloatMath.cos(theta);
float cosPhi = FloatMath.cos(phi);
float nx = cosPhi * sinTheta;
float ny = cosTheta;
float nz = sinPhi * sinTheta;
float x = radius * nx;
float y = radius * ny;
float z = radius * nz;
float u = 1.f - ((float)sliceNumber / (float)slices);
float v = (float)stackNumber / (float)stacks;
normalBuffer.put(nx);
normalBuffer.put(ny);
normalBuffer.put(nz);
vertexBuffer.put(x);
vertexBuffer.put(y);
vertexBuffer.put(z);
textureCoordBuffer.put(u);
textureCoordBuffer.put(v);
}
}
for (int stackNumber = 0; stackNumber < stacks; ++stackNumber)
{
for (int sliceNumber = 0; sliceNumber < slices; ++sliceNumber)
{
int second = (sliceNumber * (stacks + 1)) + stackNumber;
int first = second + stacks + 1;
//int first = (stackNumber * slices) + (sliceNumber % slices);
//int second = ((stackNumber + 1) * slices) + (sliceNumber % slices);
indexBuffer.put((short) first);
indexBuffer.put((short) second);
indexBuffer.put((short) (first + 1));
indexBuffer.put((short) second);
indexBuffer.put((short) (second + 1));
indexBuffer.put((short) (first + 1));
}
}
vertexBuffer.rewind();
normalBuffer.rewind();
indexBuffer.rewind();
textureCoordBuffer.rewind();
Model3D sphere = new Model3D().setVertexBuffer(vertexBuffer)
.setNormalBuffer(normalBuffer)
.setIndexBuffer(indexBuffer)
.setTexture(R.drawable.earth)
.setTextureCoordBuffer(textureCoordBuffer)
.setDiffuseLighting(-3f, 2.3f, 2f);
return sphere;
}
You're setting vertexCount as lat * lon * bytes per float, which looks very weird to me.
I think you have misnamed this variable, as the number of vertices has nothing to do with bytes per float.
You're using the same variable in glDrawArrays, which seems to me will not have the accurate number of vertices.

Categories