"We offer new support options and therefor the forums are now in read-only mode! Please check out our Support Center for more information." - Vuforia Engine Team

Android - How do I replace the Teapot?

Vuforia uses OpenGL ES for rendering 3D content. The samples show how to render simple static (non-animated) models using both OpenGL ES 1.1 and 2.0. The biggest difference between OpenGL ES 1.1 and 2.0 is the use of shaders in 2.0 instead of the fixed function pipeline in 1.1. GLES 2.0 is recommended for performance reasons, and also because some samples (e.g. BackgroundTextureAccess and VideoPlayback) only support GLES 2.0. The ImageTargets and Dominoes samples are the only ones that show a 1.1 rendering pathway. To enable GLES 1.1 rendering in these samples, open the jni/Android.mk file and set the USE_OPENGL_ES_1_1 flag to true.

 

OpenGL calls in the renderFrame method

 

We can break down the renderFrame method in ImageTargets.cpp to examine the various OpenGL calls. We'll focus on the GLES 2.0 pathway here. First, for each active (visible) trackable we create a modelview matrix from its pose. Then we apply transforms to this matrix in order to scale and position our model. Finally we multiply it by the projection matrix to create the MVP (model view projection) matrix that brings the 3D content to the screen. Later in the code, we bind this MVP matrix to the uniform variable in our shader. Each vertex of our 3D model will be multiplied by this matrix, effectively bringing that vertex from world space to screen space (the transforms are actually object > world > eye > window).

QCAR::Matrix44F modelViewMatrix = QCAR::Tool::convertPose2GLMatrix(        trackable->getPose());...

QCAR::Matrix44F modelViewProjection;SampleUtils::translatePoseMatrix(0.0f, 0.0f, kObjectScale, &modelViewMatrix.data[0]); SampleUtils::scalePoseMatrix(kObjectScale, kObjectScale, kObjectScale, &modelViewMatrix.data[0]); SampleUtils::multiplyMatrix(&projectionMatrix.data[0], &modelViewMatrix.data[0] , &modelViewProjection.data[0]);

...

glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (GLfloat*)&modelViewProjection.data[0] );

For more on positioning 3D content on the target see this article: Positioning3DContent.xml For more on OpenGL transforms see this page: http://www.songho.ca/opengl/gl_transform.html Next, we need to feed the model arrays (vertices, normals, and texture coordinates) to our shader. We start by binding our shader, then assigning our model arrays to the attribute fields in our shader (see the vertex shader in CubeShaders.h). Then we enable these attribute arrays:

glUseProgram (shaderProgramID);glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*) &teapotVertices[0]); glVertexAttribPointer(normalHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*) &teapotNormals[0]); glVertexAttribPointer(textureCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*) &teapotTexCoords[0]); glEnableVertexAttribArray (vertexHandle);glEnableVertexAttribArray (normalHandle);glEnableVertexAttribArray (textureCoordHandle);

Note that our samples don't actually make use of the normal array, but you could optionally do lighting calculations in the shader using the normals. If you want to use a model without normals simply comment out those lines. Finally, we activate the correct texture unit (typically 0 unless you have multiple textures) and bind our texture. Then we render the model using the glDrawElements call.

glActiveTexture (GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, thisTexture->mTextureID);...glDrawElements(GL_TRIANGLES, NUM_TEAPOT_OBJECT_INDEX, GL_UNSIGNED_SHORT, (const GLvoid*) &teapotIndices[0]);

Note that glDrawElements takes an array of indices. This allows us to randomly index into the other model arrays when building the triangles that make up our model. Sometimes you have a model without indices, however. This means that the model arrays are meant to be read linearly, that is, the triangles are listed in consecutive order. In that case, you would use the glDrawArrays method instead:

glDrawArrays(GL_TRIANGLES, 0, numVertices);

Swapping out the model

 

To change out the teapot model with your own model, you need to start by obtaining the vertex array for your model (and optionally texture coordinates and normals). Our samples use a simple header format to store the model data as arrays, see Teapot.h for example.

 

Many users on the forum use the perl script available at this site for turning a .obj file into a header format similar to ours: http://heikobehrens.net/2009/08/27/obj2opengl/ *Note this script generates arrays without indices, so you need to use glDrawArrays instead of glDrawElements. Also, the included banana model is of unit scale and needs to be scaled up a good bit for use with our samples. Finally, the banana texture image may need to be flipped vertically to look correct.

Once you have the arrays, you simply need to switch out the teapotVertices, teapotNormals, teapotTexCoords, and teapotIndices variables in the code above. Also change the NUM_TEAPOT_OBJECT_INDEX, and be sure to change the GL_UNSIGNED_SHORT type in the glDrawElements call if your model uses and index array with a different type. If your model doesn't have indices use glDrawArrays instead, as described above.