| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.apis.graphics; |
| |
| import javax.microedition.khronos.egl.EGLConfig; |
| import javax.microedition.khronos.opengles.GL; |
| import javax.microedition.khronos.opengles.GL10; |
| import javax.microedition.khronos.opengles.GL11ExtensionPack; |
| |
| import android.app.Activity; |
| import android.opengl.GLSurfaceView; |
| import android.opengl.GLU; |
| import android.os.Bundle; |
| import android.os.SystemClock; |
| |
| /** |
| * Demonstrate the Frame Buffer Object OpenGL ES extension. |
| * <p> |
| * This sample renders a scene into an offscreen frame buffer, and then |
| * uses the resulting image as a texture to render an onscreen scene. |
| */ |
| public class FrameBufferObjectActivity extends Activity { |
| private GLSurfaceView mGLSurfaceView; |
| |
| private class Renderer implements GLSurfaceView.Renderer { |
| private boolean mContextSupportsFrameBufferObject; |
| private int mTargetTexture; |
| private int mFramebuffer; |
| private int mFramebufferWidth = 256; |
| private int mFramebufferHeight = 256; |
| private int mSurfaceWidth; |
| private int mSurfaceHeight; |
| |
| private Triangle mTriangle; |
| private Cube mCube; |
| private float mAngle; |
| /** |
| * Setting this to true will change the behavior of this sample. It |
| * will suppress the normally onscreen rendering, and it will cause the |
| * rendering that would normally be done to the offscreen FBO |
| * be rendered onscreen instead. This can be helpful in debugging the |
| * rendering algorithm. |
| */ |
| private static final boolean DEBUG_RENDER_OFFSCREEN_ONSCREEN = false; |
| |
| public void onDrawFrame(GL10 gl) { |
| checkGLError(gl); |
| if (mContextSupportsFrameBufferObject) { |
| GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl; |
| if (DEBUG_RENDER_OFFSCREEN_ONSCREEN) { |
| drawOffscreenImage(gl, mSurfaceWidth, mSurfaceHeight); |
| } else { |
| gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFramebuffer); |
| drawOffscreenImage(gl, mFramebufferWidth, mFramebufferHeight); |
| gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); |
| drawOnscreen(gl, mSurfaceWidth, mSurfaceHeight); |
| } |
| } else { |
| // Current context doesn't support frame buffer objects. |
| // Indicate this by drawing a red background. |
| gl.glClearColor(1,0,0,0); |
| gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); |
| } |
| } |
| |
| public void onSurfaceChanged(GL10 gl, int width, int height) { |
| checkGLError(gl); |
| mSurfaceWidth = width; |
| mSurfaceHeight = height; |
| gl.glViewport(0, 0, width, height); |
| } |
| |
| public void onSurfaceCreated(GL10 gl, EGLConfig config) { |
| mContextSupportsFrameBufferObject = checkIfContextSupportsFrameBufferObject(gl); |
| if (mContextSupportsFrameBufferObject) { |
| mTargetTexture = createTargetTexture(gl, mFramebufferWidth, mFramebufferHeight); |
| mFramebuffer = createFrameBuffer(gl, mFramebufferWidth, mFramebufferHeight, mTargetTexture); |
| |
| mCube = new Cube(); |
| mTriangle = new Triangle(); |
| } |
| } |
| |
| private void drawOnscreen(GL10 gl, int width, int height) { |
| gl.glViewport(0, 0, width, height); |
| float ratio = (float) width / height; |
| gl.glMatrixMode(GL10.GL_PROJECTION); |
| gl.glLoadIdentity(); |
| gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7); |
| |
| gl.glClearColor(0,0,1,0); |
| gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); |
| gl.glBindTexture(GL10.GL_TEXTURE_2D, mTargetTexture); |
| |
| gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, |
| GL10.GL_REPLACE); |
| |
| gl.glMatrixMode(GL10.GL_MODELVIEW); |
| gl.glLoadIdentity(); |
| |
| GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); |
| |
| gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); |
| gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); |
| |
| gl.glActiveTexture(GL10.GL_TEXTURE0); |
| |
| long time = SystemClock.uptimeMillis() % 4000L; |
| float angle = 0.090f * ((int) time); |
| |
| gl.glRotatef(angle, 0, 0, 1.0f); |
| |
| mTriangle.draw(gl); |
| |
| // Restore default state so the other renderer is not affected. |
| |
| gl.glBindTexture(GL10.GL_TEXTURE_2D, 0); |
| gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); |
| gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); |
| } |
| |
| private void drawOffscreenImage(GL10 gl, int width, int height) { |
| gl.glViewport(0, 0, width, height); |
| float ratio = (float) width / height; |
| gl.glMatrixMode(GL10.GL_PROJECTION); |
| gl.glLoadIdentity(); |
| gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); |
| |
| gl.glEnable(GL10.GL_CULL_FACE); |
| gl.glEnable(GL10.GL_DEPTH_TEST); |
| |
| gl.glClearColor(0,0.5f,1,0); |
| gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); |
| gl.glMatrixMode(GL10.GL_MODELVIEW); |
| gl.glLoadIdentity(); |
| gl.glTranslatef(0, 0, -3.0f); |
| gl.glRotatef(mAngle, 0, 1, 0); |
| gl.glRotatef(mAngle*0.25f, 1, 0, 0); |
| |
| gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); |
| gl.glEnableClientState(GL10.GL_COLOR_ARRAY); |
| |
| mCube.draw(gl); |
| |
| gl.glRotatef(mAngle*2.0f, 0, 1, 1); |
| gl.glTranslatef(0.5f, 0.5f, 0.5f); |
| |
| mCube.draw(gl); |
| |
| mAngle += 1.2f; |
| |
| // Restore default state so the other renderer is not affected. |
| |
| gl.glDisable(GL10.GL_CULL_FACE); |
| gl.glDisable(GL10.GL_DEPTH_TEST); |
| gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); |
| gl.glDisableClientState(GL10.GL_COLOR_ARRAY); |
| } |
| |
| private int createTargetTexture(GL10 gl, int width, int height) { |
| int texture; |
| int[] textures = new int[1]; |
| gl.glGenTextures(1, textures, 0); |
| texture = textures[0]; |
| gl.glBindTexture(GL10.GL_TEXTURE_2D, texture); |
| gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, width, height, 0, |
| GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null); |
| gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, |
| GL10.GL_NEAREST); |
| gl.glTexParameterf(GL10.GL_TEXTURE_2D, |
| GL10.GL_TEXTURE_MAG_FILTER, |
| GL10.GL_LINEAR); |
| gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, |
| GL10.GL_REPEAT); |
| gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, |
| GL10.GL_REPEAT); |
| ; return texture; |
| } |
| |
| private int createFrameBuffer(GL10 gl, int width, int height, int targetTextureId) { |
| GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl; |
| int framebuffer; |
| int[] framebuffers = new int[1]; |
| gl11ep.glGenFramebuffersOES(1, framebuffers, 0); |
| framebuffer = framebuffers[0]; |
| gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, framebuffer); |
| |
| int depthbuffer; |
| int[] renderbuffers = new int[1]; |
| gl11ep.glGenRenderbuffersOES(1, renderbuffers, 0); |
| depthbuffer = renderbuffers[0]; |
| |
| gl11ep.glBindRenderbufferOES(GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer); |
| gl11ep.glRenderbufferStorageOES(GL11ExtensionPack.GL_RENDERBUFFER_OES, |
| GL11ExtensionPack.GL_DEPTH_COMPONENT16, width, height); |
| gl11ep.glFramebufferRenderbufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, |
| GL11ExtensionPack.GL_DEPTH_ATTACHMENT_OES, |
| GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer); |
| |
| gl11ep.glFramebufferTexture2DOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, |
| GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, GL10.GL_TEXTURE_2D, |
| targetTextureId, 0); |
| int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES); |
| if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) { |
| throw new RuntimeException("Framebuffer is not complete: " + |
| Integer.toHexString(status)); |
| } |
| gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0); |
| return framebuffer; |
| } |
| |
| private boolean checkIfContextSupportsFrameBufferObject(GL10 gl) { |
| return checkIfContextSupportsExtension(gl, "GL_OES_framebuffer_object"); |
| } |
| |
| /** |
| * This is not the fastest way to check for an extension, but fine if |
| * we are only checking for a few extensions each time a context is created. |
| * @param gl |
| * @param extension |
| * @return true if the extension is present in the current context. |
| */ |
| private boolean checkIfContextSupportsExtension(GL10 gl, String extension) { |
| String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " "; |
| // The extensions string is padded with spaces between extensions, but not |
| // necessarily at the beginning or end. For simplicity, add spaces at the |
| // beginning and end of the extensions string and the extension string. |
| // This means we can avoid special-case checks for the first or last |
| // extension, as well as avoid special-case checks when an extension name |
| // is the same as the first part of another extension name. |
| return extensions.indexOf(" " + extension + " ") >= 0; |
| } |
| } |
| |
| static void checkGLError(GL gl) { |
| int error = ((GL10) gl).glGetError(); |
| if (error != GL10.GL_NO_ERROR) { |
| throw new RuntimeException("GLError 0x" + Integer.toHexString(error)); |
| } |
| } |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| // Create our surface view and set it as the content of our |
| // Activity |
| mGLSurfaceView = new GLSurfaceView(this); |
| mGLSurfaceView.setRenderer(new Renderer()); |
| setContentView(mGLSurfaceView); |
| } |
| |
| @Override |
| protected void onResume() { |
| // Ideally a game should implement onResume() and onPause() |
| // to take appropriate action when the activity looses focus |
| super.onResume(); |
| mGLSurfaceView.onResume(); |
| } |
| |
| @Override |
| protected void onPause() { |
| // Ideally a game should implement onResume() and onPause() |
| // to take appropriate action when the activity looses focus |
| super.onPause(); |
| mGLSurfaceView.onPause(); |
| } |
| } |