Android OpenGL ES2 Skeletal animation child bone matrix - java

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

Related

How to change specific part of vbo?

I have recently created 2D height map grid which generates 3D terrain mesh for my world With the ability to add hills/bumps with mouse click events during runtime. My Problem is that every time i add to the height of the vertices i update the whole terrain's normal and position vbos(very not efficient). what is the way to
change specific part of vbo?
I have heared that glBufferSubData is the way, but How can i change only the Y value? (the vbo is x,y,z,x,y,z...)
and get the changed verticies in order for glBufferSubData?
Terrain class:
public class Terrain {
public static final int SIZE = 500;
//VAO, vertexCount, VBOS
private RawModel model;
//textures for the terrain
private terrainTexturePack texturePack;
Loader loader;
private static int VERTEX_COUNT =128;
float[] Vertices;
float[] Normals;
float[] TextureCoords;
int[] Indices;
private float[][] heights;
public Terrain(Loader loader, terrainTexturePack texturePack) {
this.texturePack = texturePack;
this.loader = loader;
this.model = generateTerrain(loader);
}
public RawModel getModel() {
return model;
}
public terrainTexturePack getTexturePack() {
return texturePack;
}
//player collision detection witn the terrain
public Vector3f getXYZOfTerrain(float worldX, float worldZ) {
float gridSquareSize = SIZE / ((float) heights.length - 1);
int gridX = (int) Math.floor(worldX / gridSquareSize);
int gridZ = (int) Math.floor(worldZ / gridSquareSize);
if(gridX >= heights.length - 1 || gridZ >= heights.length - 1 || gridX < 0 || gridZ < 0) {
return null;
}
float xCoord = (worldX % gridSquareSize)/gridSquareSize;
float zCoord = (worldZ % gridSquareSize)/gridSquareSize;
float yCoord;
if (xCoord <= (1-zCoord)) {
yCoord = Maths.barryCentric(new Vector3f(0, heights[gridX][gridZ], 0), new Vector3f(1,
heights[gridX + 1][gridZ], 0), new Vector3f(0,
heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
} else {
yCoord = Maths.barryCentric(new Vector3f(1, heights[gridX + 1][gridZ], 0), new Vector3f(1,
heights[gridX + 1][gridZ + 1], 1), new Vector3f(0,
heights[gridX][gridZ + 1], 1), new Vector2f(xCoord, zCoord));
}
return new Vector3f(gridX, yCoord, gridZ);
}
//GENERATE THE TERRAIN
private RawModel generateTerrain(Loader loader) {
int pointer = 0;
int count = VERTEX_COUNT * VERTEX_COUNT;
heights = new float[VERTEX_COUNT][VERTEX_COUNT];
float[] vertices = new float[count * 3];
float[] normals = new float[count * 3];
float[] textureCoords = new float[count * 2];
int[] indices = new int[6 * (VERTEX_COUNT - 1) * (VERTEX_COUNT * 1)];
int vertexPointer = 0;
for (int i = 0; i < VERTEX_COUNT; i++) {
for (int j = 0; j < VERTEX_COUNT; j++) {
vertices[vertexPointer * 3] = (float) j / ((float) VERTEX_COUNT - 1) * SIZE;
float height = 0f;
vertices[vertexPointer * 3 + 1] = height;
heights[j][i] = height;
vertices[vertexPointer * 3 + 2] = (float) i / ((float) VERTEX_COUNT - 1) * SIZE;
Vector3f normal =new Vector3f(0, 1, 0);// calculateNormal(j, i, noise);
normals[vertexPointer * 3] = normal.x;
normals[vertexPointer * 3 + 1] = normal.y;
normals[vertexPointer * 3 + 2] = normal.z;
textureCoords[vertexPointer * 2] = (float) j / ((float) VERTEX_COUNT - 1);
textureCoords[vertexPointer * 2 + 1] = (float) i / ((float) VERTEX_COUNT - 1);
vertexPointer++;
if(i < VERTEX_COUNT - 1 && j < VERTEX_COUNT - 1){
int topLeft = (i * VERTEX_COUNT) + j;
int topRight = topLeft + 1;
int bottomLeft = ((i + 1) * VERTEX_COUNT) + j;
int bottomRight = bottomLeft + 1;
indices[pointer++] = topLeft;
indices[pointer++] = bottomLeft;
indices[pointer++] = topRight;
indices[pointer++] = topRight;
indices[pointer++] = bottomLeft;
indices[pointer++] = bottomRight;
}
}
}
Vertices = vertices;
TextureCoords = textureCoords;
Normals = normals;
Indices = indices;
return loader.loadToVAO(vertices, textureCoords, normals, indices);
}
//Calculate normal
private Vector3f calculateNormal(int x, int z) {
float heightL = Vertices[((( (z) *VERTEX_COUNT)+ (x-1) )*3)+1];
float heightR = Vertices[((( (z) *VERTEX_COUNT)+ (x+1) )*3)+1];
float heightD = Vertices[((( (z-1) *VERTEX_COUNT)+ (x) )*3)+1];
float heightU = Vertices[((( (z+1) *VERTEX_COUNT)+ (x) )*3)+1];
Vector3f normal = new Vector3f(heightL - heightR, 2f, heightD - heightU);
normal.normalise();
return normal;
}
//create mountain where the mouse clicked
//Vertices[(((y*VERTEX_COUNT)+x)*3)+1] = one Vertex in 2d grid
public void createHill(int x0, int y0){
float h = 0.06f;
int xs=VERTEX_COUNT;
int ys=VERTEX_COUNT;
float maxHeight =Vertices[(((y0*xs)+x0)*3)+1]+h;
float r = (9*maxHeight)/30;
//Loop the vertices
for(int y=(int) (y0-r);y<=y0+r;y++)
for(int x=(int) (x0-r);x<=x0+r;x++){
double circule = Math.sqrt((x-x0)*(x-x0)+(y0-y)*(y0-y));
if (circule <= r)
if ((x>=1)&&(x<xs-1))
if ((y>=1)&&(y<ys-1)){
Vertices[(((y*xs)+x)*3)+1] = Maths.hillsHeight(x0, x, y0, y,(maxHeight), r);
Vector3f normal = calculateNormal(x,y);
Normals[((((y*xs)+x))) * 3] = normal.x;
Normals[((((y*xs)+x))) * 3 + 1] = normal.y;
Normals[((((y*xs)+x))) * 3 + 2] = normal.z;
}
}
//change the whole VBO's not effective
//Note: i know that i dont need to update textures and indices
this.model=loader.loadToVAO(Vertices, TextureCoords, Normals, Indices);
}
}
Raw model class(vbo and vao holder):
//Store the VAOS and VBOS
public class RawModel {
private int vaoID;
private int vertexCount;
private int positionVbo;
private int normalVbo;
private int textureVbo;
public RawModel(int vaoID, int vertexCount, int positionVbo, int normalVbo, int textureVbo) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
this.positionVbo = positionVbo;
this.normalVbo = normalVbo;
this.textureVbo = textureVbo;
}
public RawModel(int vaoID, int vertexCount) {
this.vaoID = vaoID;
this.vertexCount = vertexCount;
}
public int getVaoID() {
return vaoID;
}
public int getVertexCount() {
return vertexCount;
}
public int getPositionVbo() {
return positionVbo;
}
public int getTextureVbo() {
return textureVbo;
}
public int getNormalVbo() {
return normalVbo;
}
}
loader class:
public class Loader {
//For clean up
private List<Integer> vaos = new ArrayList<Integer>();
private List<Integer> vbos = new ArrayList<Integer>();
private List<Integer> textures = new ArrayList<Integer>();
//Load mesh into VAO
public RawModel loadToVAO(float[] positions,float[] textureCoords,float[] normals,int[] indices){
int vaoID = createVAO();
bindIndicesBuffer(indices);
int positionvbo = storeDataInAttributeList(0,3,positions);
int textureVbo = storeDataInAttributeList(1,2,textureCoords);
int normalsnvbo = storeDataInAttributeList(2,3,normals);
unbindVAO();
return new RawModel(vaoID,indices.length, positionvbo, textureVbo, normalsnvbo);
}
//Load texture
public int loadTexture(String fileName) {
Texture texture = null;
try {
texture = TextureLoader.getTexture("PNG",
new FileInputStream("res/textures/" + fileName + ".png"));
GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
GL11.GL_LINEAR_MIPMAP_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL14.GL_TEXTURE_LOD_BIAS, -2);
if(GLContext.getCapabilities().GL_EXT_texture_filter_anisotropic){
float amount = Math.min(4f,
GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT));
GL11.glTexParameterf(GL11.GL_TEXTURE_2D,
EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, amount);
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Tried to load texture " + fileName + ".png , didn't work");
System.exit(-1);
}
textures.add(texture.getTextureID());
return texture.getTextureID();
}
//Clean up
public void cleanUp(){
for(int vao:vaos){
GL30.glDeleteVertexArrays(vao);
}
for(int vbo:vbos){
GL15.glDeleteBuffers(vbo);
}
for(int texture:textures){
GL11.glDeleteTextures(texture);
}
}
//Creates vao
private int createVAO(){
int vaoID = GL30.glGenVertexArrays();
vaos.add(vaoID);
GL30.glBindVertexArray(vaoID);
return vaoID;
}
//Store data in vbo
private int storeDataInAttributeList(int attributeNumber, int coordinateSize,float[] data){
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
FloatBuffer buffer = storeDataInFloatBuffer(data);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(attributeNumber,coordinateSize,GL11.GL_FLOAT,false,0,0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
return vboID;
}
private void unbindVAO(){
GL30.glBindVertexArray(0);
}
//Bind indices buffer
private void bindIndicesBuffer(int[] indices){
int vboID = GL15.glGenBuffers();
vbos.add(vboID);
GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboID);
IntBuffer buffer = storeDataInIntBuffer(indices);
GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
}
//Store in int buffer
private IntBuffer storeDataInIntBuffer(int[] data){
IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
//Store in float buffer
private FloatBuffer storeDataInFloatBuffer(float[] data){
FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
buffer.put(data);
buffer.flip();
return buffer;
}
//Load skyBox textures
public int loadCubeMap(String[] textureFiles){
int texID = GL11.glGenTextures();
GL13.glActiveTexture(GL13.GL_TEXTURE0);
GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texID);
for(int i = 0; i < textureFiles.length; i++){
TextureData data = decodeTextureFile("res/textures/"+ textureFiles[i] + ".png");
GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 0,
GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data.getBuffer());
}
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
textures.add(texID);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
return texID;
}
private TextureData decodeTextureFile(String fileName) {
int width = 0;
int height = 0;
ByteBuffer buffer = null;
try {
FileInputStream in = new FileInputStream(fileName);
PNGDecoder decoder = new PNGDecoder(in);
width = decoder.getWidth();
height = decoder.getHeight();
buffer = ByteBuffer.allocateDirect(4 * width * height);
decoder.decode(buffer, width * 4, Format.RGBA);
buffer.flip();
in.close();
} catch (Exception e) {
e.printStackTrace();
System.err.println("Tried to load texture " + fileName + ", didn't work");
System.exit(-1);
}
return new TextureData(buffer, width, height);
}
//Load textures for GUI
public RawModel loadToVAO(float[] positions, int dimensions) {
int vaoID = createVAO();
this.storeDataInAttributeList(0, dimensions, positions);
unbindVAO();
return new RawModel(vaoID, positions.length / dimensions);
}
}
Solved Thank to Reto Koradi
public void changeVbo(int position, float[] data, int VboId){
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, VboId);
FloatBuffer ArrayData = storeDataInFloatBuffer(data);
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER,position * 4, ArrayData);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
}
The easiest and likely most efficient way is to store the heights (y-values) in a separate VBO, and specify them as a separate vertex attribute.
Then, in your vertex shader code, you can simply reassemble the position from the separate attributes. You might have something like this in your shader code now:
in vec3 pos;
This changes to:
in vec3 posXZ;
in float posY;
...
vec3 pos = vec3(posXZ.x, posY, posXZ.y);
Using a separate VBO for data that changes frequently also allows you to specify the allocation flags accordingly. You can use GL_DYNAMIC_DRAW for the data that changes frequently, GL_STATIC_DRAW for the rest.
Another option would be to use glMapBuffer(). This gives you a CPU pointer to the buffer content, allowing you to modify only the data that you actually want to change. However, you'll have to be careful that you don't introduce undesirable synchronization between CPU and GPU. The glMapBuffer() call might block until the GPU finished all rendering calls using the previous content of the buffer. One common technique is to use multiple copies of the data in a set of buffers, and cycle through them, to minimize synchronization. But if the amount of data is large, that will obviously cause memory usage to increase dramatically.
In your use case, I suspect that you'll have to update the normals as well, since they depend on the height values.

OpenGL / Shader / Matrix : Weird scaling of the shape after a rotation of the view matrix

I try to display a triangle using OpenGL with Shader, and I have a problem when I move the camera. The orientation of my triangle seems ok, but the x and y scaling looks funny...
The two following images illustrate my problem : the left side of the image is the output I have using the shaders, and the right part is the representation of the same scene using JOGL functions (such as "glLookAt" / "glFrustum"). The two triangles should be the same, but it is not the case when I rotate the camera.
Here is the "main" of my vertex shader :
void main(void) {
varying_Color = attribute_Color;
gl_Position = projectionMatrix * viewMatrix * vec4(attribute_Position,1.0);
}
Here is how I compute my projection matrix :
final int height = getSurfaceHeight();
final double aspect = getSurfaceWidth() / height;
final double maxDim = getMaxEnvDim();
final double zNear = maxDim / 1000;
final double zFar = maxDim*10;
final double frustum_length = zFar - zNear;
double fW, fH;
final double fovY = 45;
if (aspect > 1.0) {
fH = FastMath.tan(fovY / 360 * Math.PI) * zNear;
fW = fH * aspect;
} else {
fW = FastMath.tan(fovY / 360 * Math.PI) * zNear;
fH = fW / aspect;
}
projectionMatrix = new Matrix4f();
projectionMatrix.m00 = (float) (zNear / fW);
projectionMatrix.m11 = (float) (zNear / fH);
projectionMatrix.m22 = (float) -((zFar + zNear) / frustum_length);
projectionMatrix.m23 = -1;
projectionMatrix.m32 = (float) -((2 * zNear * zFar) / frustum_length);
projectionMatrix.m33 = 0;
Here is how I compute my view matrix :
public static double[] CrossProduct(final double[] vect1, final double[] vect2) {
final double[] result = new double[3];
result[0] = vect1[1] * vect2[2] - vect1[2] * vect2[1];
result[1] = vect1[2] * vect2[0] - vect1[0] * vect2[2];
result[2] = vect1[0] * vect2[1] - vect1[1] * vect2[0];
return result;
}
public static double ScalarProduct(final double[] vect1, final double[] vect2) {
return vect1[0]*vect2[0]+vect1[1]*vect2[1]+vect1[2]*vect2[2];
}
public static double[] Normalize(final double[] vect) {
double[] result = new double[vect.length];
double sum = 0;
for (int i = 0; i < vect.length ; i++) {
sum += Math.abs(vect[i]);
}
for (int i = 0; i < vect.length ; i++) {
result[i] = vect[i] / sum;
}
return result;
}
public static Matrix4f createViewMatrix(ICamera camera) {
// see http://in2gpu.com/2015/05/17/view-matrix/
Matrix4f viewMatrix = new Matrix4f();
double[] fVect = new double[3]; // forward vector : direction vector of the camera.
double[] sVect = new double[3]; // orthogonal vector : "right" or "sideways" vector.
double[] vVect = new double[3]; // cross product between f and s.
double[] pVect = new double[3]; // camera position.
double sum = Math.abs(camera.getTarget().x - camera.getPosition().x)
+ Math.abs(camera.getTarget().y - camera.getPosition().y)
+ Math.abs(camera.getTarget().z - camera.getPosition().z);
fVect[0] = -(camera.getTarget().x - camera.getPosition().x) / sum;
fVect[1] = (camera.getTarget().y - camera.getPosition().y) / sum;
fVect[2] = -(camera.getTarget().z - camera.getPosition().z) / sum;
double[] crossProduct = CrossProduct(fVect,new double[]{camera.getOrientation().x,
-camera.getOrientation().y,camera.getOrientation().z});
sVect = Normalize(crossProduct);
vVect = CrossProduct(sVect,fVect);
pVect = new double[]{camera.getPosition().x,
-camera.getPosition().y,camera.getPosition().z};
viewMatrix.m00 = (float) sVect[0];
viewMatrix.m01 = (float) sVect[1];
viewMatrix.m02 = (float) sVect[2];
viewMatrix.m03 = (float) -ScalarProduct(sVect,pVect);
viewMatrix.m10 = (float) vVect[0];
viewMatrix.m11 = (float) vVect[1];
viewMatrix.m12 = (float) vVect[2];
viewMatrix.m13 = (float) -ScalarProduct(vVect,pVect);
viewMatrix.m20 = (float) fVect[0];
viewMatrix.m21 = (float) fVect[1];
viewMatrix.m22 = (float) fVect[2];
viewMatrix.m23 = (float) -ScalarProduct(fVect,pVect);
viewMatrix.m30 = (float) 0;
viewMatrix.m31 = (float) 0;
viewMatrix.m32 = (float) 0;
viewMatrix.m33 = (float) 1;
viewMatrix.transpose();
return viewMatrix;
}
Notice that when I use a simple transformation matrix instead of the view matrix and if I apply a rotation to my triangle, it appears right. That leads me to the fact that the problem is from my view matrix, but I can't find my mistake...
Notice also that I have some weird "-y" to get the view matrix, because my openGL world is an indirect coordinate system.
Thanks in advance !
Your Normalize function is strange. You are not computing the Euclidean length of your vector but its uniform norm.
Try changing it to this:
double[] result = new double[vect.length];
double sum = 0;
for (int i = 0; i < vect.length ; i++) {
sum += Math.pow(vect[i], 2);
}
for (int i = 0; i < vect.length ; i++) {
result[i] = vect[i] / Math.sqrt(sum);
}
return result;
You also have the exact same problem in your createViewMatrix function at this line:
double sum = Math.abs(camera.getTarget().x - camera.getPosition().x) +
Math.abs(camera.getTarget().y - camera.getPosition().y) +
Math.abs(camera.getTarget().z - camera.getPosition().z);
Change it to:
double sum = Math.sqrt(Math.pow(camera.getTarget().x - camera.getPosition().x, 2) +
Math.pow(camera.getTarget().y - camera.getPosition().y, 2) +
Math.pow(camera.getTarget().z - camera.getPosition().z, 2));

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

Texture Mapping is reversed! Java3D

I could use some extra eyes to help me see why my texture map is reversed on my object. It prints backwards -- as if seeing it in a mirror. I had suspected that maybe reversing the indices would help, but that just changed where it was mapped without reversing the reversal.
public class MobiusBanner extends Applet {
public static void main(String[] args) {
new MainFrame(new MobiusBanner(), 800, 600);
}
#Override
public void init() {
GraphicsConfiguration gc = SimpleUniverse.getPreferredConfiguration();
Canvas3D canvas = new Canvas3D(gc);
this.setLayout(new BorderLayout());
this.add(canvas, BorderLayout.CENTER);
SimpleUniverse su = new SimpleUniverse(canvas);
su.getViewingPlatform().setNominalViewingTransform();
BranchGroup bg = createSceneGraph();
bg.compile();
su.addBranchGraph(bg);
}
private BranchGroup createSceneGraph() {
BranchGroup root = new BranchGroup();
Shape3D shape = new Shape3D();
shape.setGeometry(mobius().getIndexedGeometryArray());
//Scaling transform
Transform3D tr = new Transform3D();
tr.setScale(0.5);
//Spin transform group
TransformGroup spin = new TransformGroup();
spin.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
spin.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
root.addChild(spin);
//Set appearance
Appearance ap = createTextureAppearance();
ap.setPolygonAttributes(new PolygonAttributes(PolygonAttributes.POLYGON_FILL,
PolygonAttributes.CULL_BACK, 0));
//Set materials
Material mat = new Material();
mat.setLightingEnable(true);
mat.setShininess(30);
ap.setMaterial(mat);
//Overarching Transform group
TransformGroup tg = new TransformGroup(tr);
tg.addChild(shape);
spin.addChild(tg);
shape.setAppearance(ap);
//Set rotation
MouseRotate rotator = new MouseRotate(spin);
BoundingSphere bounds = new BoundingSphere();
rotator.setSchedulingBounds(bounds);
spin.addChild(rotator);
//Set translation
MouseTranslate translator = new MouseTranslate(spin);
translator.setSchedulingBounds(bounds);
spin.addChild(translator);
//Set zoom
MouseZoom zoom = new MouseZoom(spin);
zoom.setSchedulingBounds(bounds);
spin.addChild(zoom);
//Set background
Background background = new Background(1.0f, 1.0f, 1.0f);
background.setApplicationBounds(bounds);
root.addChild(background);
f//Set lighting
AmbientLight light = new AmbientLight(true, new Color3f(Color.BLACK));
light.setInfluencingBounds(bounds);
root.addChild(light);
PointLight ptlight = new PointLight(new Color3f(Color.white),
new Point3f(0.5f, 0.5f, 1f),
new Point3f(1f, 0.2f, 0f));
ptlight.setInfluencingBounds(bounds);
root.addChild(ptlight);
return root;
}//Close branchgroup method
//Create the Mobius shape
private GeometryInfo mobius() {
int m = 100; //number of row points
int n = 100; //number of col points
int p = 4 * ((m - 1) * (n - 1)); //faces * points per face
IndexedQuadArray iqa = new IndexedQuadArray(m * n,
GeometryArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2, p);
Point3d[] vertices = new Point3d[m * n];
int index = 0;
//Create vertices
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
double u = i * (4 * (Math.PI)) / (m - 1);
double v = -0.3 + (j * (0.6 / (n - 1)));
double x = (1 + v * Math.cos(u / 2)) * Math.cos(u);
double y = (1 + v * Math.cos(u / 2)) * Math.sin(u);
double z = v * Math.sin(u / 2);
vertices[index] = new Point3d(x, y, z);
index++;
}//close nested for loop
}//close for loop
index = 0;
//set texture coordinates
TexCoord2f[] tex = new TexCoord2f[m * n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
tex[index] = new TexCoord2f();
tex[index] = new TexCoord2f(i * 1f / m, j * 1f / n);
index++;
}
}
iqa.setCoordinates(0, vertices);
iqa.setTextureCoordinates(0, 0, tex);
index = 0;
//set index for coordinates
int[] texIndices = new int[p];
for (int i = 0; i < m - 1; i++) {
for (int j = 0; j < n - 1; j++) {
iqa.setCoordinateIndex(index, i * m + j);
texIndices[index] = i * m + j;
index++;
iqa.setCoordinateIndex(index, i * m + j + 1);
texIndices[index] = i * m + j + 1;
index++;
iqa.setCoordinateIndex(index, (i + 1) * m + j + 1);
texIndices[index] = (i + 1) * m + j + 1;
index++;
iqa.setCoordinateIndex(index, (i + 1) * m + j);
texIndices[index] = (i + 1) * m + j;
index++;
}//close nested for loop
}//close for loop
iqa.setTextureCoordinateIndices(0, 0, texIndices);
//create geometry info and generate normals for shape
GeometryInfo gi = new GeometryInfo(iqa);
NormalGenerator ng = new NormalGenerator();
ng.generateNormals(gi);
return gi;
}
Appearance createTextureAppearance() {
Appearance ap = new Appearance();
BufferedImage bi = new BufferedImage(1024, 128,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) bi.getGraphics();
g2.setColor(Color.white);
g2.fillRect(0, 0, 1024, 128);
g2.setFont(new Font("Serif", Font.BOLD, 36));
g2.setColor(new Color(200, 0, 0));
g2.drawString("Mobius Strip", 0, 100);
ImageComponent2D image = new ImageComponent2D(ImageComponent2D.FORMAT_RGBA, bi);
Texture2D texture = new Texture2D(Texture.BASE_LEVEL, Texture.RGBA,
image.getWidth(), image.getHeight());
texture.setImage(0, image);
texture.setMagFilter(Texture.BASE_LEVEL_LINEAR);
ap.setTexture(texture);
//Combine Texture and Lighting
TextureAttributes textatt = new TextureAttributes();
textatt.setTextureMode(TextureAttributes.COMBINE);
ap.setTextureAttributes(textatt);
ap.setMaterial(new Material());
return ap;
}
}
You want to reverse only one of the texture coordinates, not both. Try changing
tex[index] = new TexCoord2f(i * 1f / m, j * 1f / n);
to
tex[index] = new TexCoord2f((m-1-i) * 1f / m, j * 1f / n);
or
tex[index] = new TexCoord2f(i * 1f / m, (n-1-j) * 1f / n);

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