Android OpenGL es 2.0 Can not reload textures on object.

1.5k views Asked by At

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!

2

There are 2 answers

0
Reto Koradi On BEST ANSWER

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 your GLSurfaceView.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 the onDraw() method. Another convenient option is using the queueEvent() method on the view, which allows you to pass in a Runnable that will later be executed in the rendering thread.

0
Björn Kechel On

I just ran into the same problem.

Reto Koradi explained it great but I wanted to shared my solution: I used queueEvent with a Runnable in the GLSurfaceView.

public void addTexture(final int textureResId) {
    queueEvent(new Runnable() {
        @Override
        public void run() {
            mRenderer.loadTexture(textureResId);
            // or different GL thread tasks like clearing the texture
        }
    });
}