I've managed to load textures and free rotate a sphere thanks to several tutorials, questions and answers asked here but i stumbled upon the need of texture reloading at runtime (get a bitmap image, process it and then apply it as a new texture to an object). I didnt find any working solutions for my specific problem (i've read all related questions and answers). Im quite new to OpenGL. This is my second project, my first 3D one and my first question asked here. So here it goes:
Basicaly the texture loading is done in the following function:
public void loadGLTexture(final Context context, final int texture) {
GLES20.glGenTextures(1, mTextures, 0);
if (mTextures[0] != 0)
{
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), texture, options);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
if (mTextures[0] == 0)
{
throw new RuntimeException("Texture load fail");
}
}
While the draw is done in this function:
public void draw(float[] mvpMatrix) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, this.mTextures[0]);
GLES20.glFrontFace(GLES20.GL_CW);
for (int i = 0; i < this.mTotalNumStrips; i++) {
//vertex
this.mVertexBuffer.get(i).position(0);
GLES20.glVertexAttribPointer(mPositionHandle, NUM_FLOATS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, this.mVertexBuffer.get(i));
GLES20.glEnableVertexAttribArray(mPositionHandle);
//texture
this.mTextureBuffer.get(i).position(0);
GLES20.glVertexAttribPointer(mTextureCoordinateHandle, NUM_FLOATS_PER_TEXTURE,
GLES20.GL_FLOAT, false,
textureStride, this.mTextureBuffer.get(i));
GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle);
//draw strip
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, this.mVertices.get(i).length / NUM_FLOATS_PER_VERTEX);
}
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
// Disable the client state before leaving.
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle);
}
In the above function i use the Strips tehnique to render a sphere. For each strip i have to load the texture and vertex data and finaly draw it.
I also have a function that should delete the textures that does nothing more than:
public void clearGLTextures(){
GLES20.glDeleteTextures(1, mTextures, 0);
}
What i want to achieve here is texture reloading so the plan was:
INITIALISE(works): loadGLTexture(initialtexture) -> loop the draw() function
RELOAD(does not work): clearGLTextures() -> loadGLTexture(newTexture) -> loop the draw() function
So not only that i cant reload the texture but also the call to clearGLTextures() seems not to work because the initialtexture remains on scren.
Any thoughts are welcome, Thanks!
This is an example of a very common kind of problem when doing OpenGL programming on Android. Unfortunately the symptoms are very variable, so the questions are not really duplicates.
When you use
GLSurfaceView
, it creates a separate rendering thread. All the methods in yourGLSurfaceView.Renderer
implementation (onSurfaceCreated
,onSurfaceChanged
,onDrawFrame
) are called in this rendering thread.The second part of the puzzle is that you need a current OpenGL context to make OpenGL calls. And OpenGL contexts are current per thread.
GLSurfaceView
takes care of creating a context, and making it current in the rendering thread.As a consequence, you can't make any OpenGL calls from other threads (unless you do much more work, and add complexity). The most common error is to try making OpenGL calls from the UI thread, typically when handling user input like touch events. Those calls will not work.
There are a number of options if you want to execute OpenGL calls in response to user input. For example, you can set members in the
Renderer
that describe the necessary state changes, and are checked in theonDraw()
method. Another convenient option is using thequeueEvent()
method on the view, which allows you to pass in aRunnable
that will later be executed in the rendering thread.