1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.apis.graphics;
18 
19 import javax.microedition.khronos.egl.EGLConfig;
20 import javax.microedition.khronos.opengles.GL;
21 import javax.microedition.khronos.opengles.GL10;
22 import javax.microedition.khronos.opengles.GL11ExtensionPack;
23 
24 import android.app.Activity;
25 import android.opengl.GLSurfaceView;
26 import android.opengl.GLU;
27 import android.os.Bundle;
28 import android.os.SystemClock;
29 
30 /**
31  * Demonstrate the Frame Buffer Object OpenGL ES extension.
32  * <p>
33  * This sample renders a scene into an offscreen frame buffer, and then
34  * uses the resulting image as a texture to render an onscreen scene.
35  */
36 public class FrameBufferObjectActivity extends Activity {
37     private GLSurfaceView mGLSurfaceView;
38 
39     private class Renderer implements GLSurfaceView.Renderer {
40         private boolean mContextSupportsFrameBufferObject;
41         private int mTargetTexture;
42         private int mFramebuffer;
43         private int mFramebufferWidth = 256;
44         private int mFramebufferHeight = 256;
45         private int mSurfaceWidth;
46         private int mSurfaceHeight;
47 
48         private Triangle mTriangle;
49         private Cube mCube;
50         private float mAngle;
51         /**
52          * Setting this to true will change the behavior  of this sample. It
53          * will suppress the normally onscreen rendering, and it will cause the
54          * rendering that would normally be done to the offscreen FBO
55          * be rendered onscreen instead. This can be helpful in debugging the
56          * rendering algorithm.
57          */
58         private static final boolean DEBUG_RENDER_OFFSCREEN_ONSCREEN = false;
59 
onDrawFrame(GL10 gl)60         public void onDrawFrame(GL10 gl) {
61             checkGLError(gl);
62             if (mContextSupportsFrameBufferObject) {
63                 GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;
64                 if (DEBUG_RENDER_OFFSCREEN_ONSCREEN) {
65                     drawOffscreenImage(gl, mSurfaceWidth, mSurfaceHeight);
66                 } else {
67                     gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFramebuffer);
68                     drawOffscreenImage(gl, mFramebufferWidth, mFramebufferHeight);
69                     gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
70                     drawOnscreen(gl, mSurfaceWidth, mSurfaceHeight);
71                 }
72             } else {
73                 // Current context doesn't support frame buffer objects.
74                 // Indicate this by drawing a red background.
75                 gl.glClearColor(1,0,0,0);
76                 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
77             }
78         }
79 
onSurfaceChanged(GL10 gl, int width, int height)80         public void onSurfaceChanged(GL10 gl, int width, int height) {
81             checkGLError(gl);
82             mSurfaceWidth = width;
83             mSurfaceHeight = height;
84             gl.glViewport(0, 0, width, height);
85         }
86 
onSurfaceCreated(GL10 gl, EGLConfig config)87         public void onSurfaceCreated(GL10 gl, EGLConfig config) {
88             mContextSupportsFrameBufferObject = checkIfContextSupportsFrameBufferObject(gl);
89             if (mContextSupportsFrameBufferObject) {
90                 mTargetTexture = createTargetTexture(gl, mFramebufferWidth, mFramebufferHeight);
91                 mFramebuffer = createFrameBuffer(gl, mFramebufferWidth, mFramebufferHeight, mTargetTexture);
92 
93                 mCube = new Cube();
94                 mTriangle = new Triangle();
95             }
96         }
97 
drawOnscreen(GL10 gl, int width, int height)98         private void drawOnscreen(GL10 gl, int width, int height) {
99             gl.glViewport(0, 0, width, height);
100             float ratio = (float) width / height;
101             gl.glMatrixMode(GL10.GL_PROJECTION);
102             gl.glLoadIdentity();
103             gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
104 
105             gl.glClearColor(0,0,1,0);
106             gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
107             gl.glBindTexture(GL10.GL_TEXTURE_2D, mTargetTexture);
108 
109             gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
110                     GL10.GL_REPLACE);
111 
112             gl.glMatrixMode(GL10.GL_MODELVIEW);
113             gl.glLoadIdentity();
114 
115             GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
116 
117             gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
118             gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
119 
120             gl.glActiveTexture(GL10.GL_TEXTURE0);
121 
122             long time = SystemClock.uptimeMillis() % 4000L;
123             float angle = 0.090f * ((int) time);
124 
125             gl.glRotatef(angle, 0, 0, 1.0f);
126 
127             mTriangle.draw(gl);
128 
129             // Restore default state so the other renderer is not affected.
130 
131             gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
132             gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
133             gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
134         }
135 
drawOffscreenImage(GL10 gl, int width, int height)136         private void drawOffscreenImage(GL10 gl, int width, int height) {
137             gl.glViewport(0, 0, width, height);
138             float ratio = (float) width / height;
139             gl.glMatrixMode(GL10.GL_PROJECTION);
140             gl.glLoadIdentity();
141             gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
142 
143             gl.glEnable(GL10.GL_CULL_FACE);
144             gl.glEnable(GL10.GL_DEPTH_TEST);
145 
146             gl.glClearColor(0,0.5f,1,0);
147             gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
148             gl.glMatrixMode(GL10.GL_MODELVIEW);
149             gl.glLoadIdentity();
150             gl.glTranslatef(0, 0, -3.0f);
151             gl.glRotatef(mAngle,        0, 1, 0);
152             gl.glRotatef(mAngle*0.25f,  1, 0, 0);
153 
154             gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
155             gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
156 
157             mCube.draw(gl);
158 
159             gl.glRotatef(mAngle*2.0f, 0, 1, 1);
160             gl.glTranslatef(0.5f, 0.5f, 0.5f);
161 
162             mCube.draw(gl);
163 
164             mAngle += 1.2f;
165 
166             // Restore default state so the other renderer is not affected.
167 
168             gl.glDisable(GL10.GL_CULL_FACE);
169             gl.glDisable(GL10.GL_DEPTH_TEST);
170             gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
171             gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
172         }
173 
createTargetTexture(GL10 gl, int width, int height)174         private int createTargetTexture(GL10 gl, int width, int height) {
175             int texture;
176             int[] textures = new int[1];
177             gl.glGenTextures(1, textures, 0);
178             texture = textures[0];
179             gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
180             gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, width, height, 0,
181                     GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null);
182             gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
183                     GL10.GL_NEAREST);
184             gl.glTexParameterf(GL10.GL_TEXTURE_2D,
185                     GL10.GL_TEXTURE_MAG_FILTER,
186                     GL10.GL_LINEAR);
187             gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
188                     GL10.GL_REPEAT);
189             gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
190                     GL10.GL_REPEAT);
191 ;            return texture;
192         }
193 
createFrameBuffer(GL10 gl, int width, int height, int targetTextureId)194         private int createFrameBuffer(GL10 gl, int width, int height, int targetTextureId) {
195             GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;
196             int framebuffer;
197             int[] framebuffers = new int[1];
198             gl11ep.glGenFramebuffersOES(1, framebuffers, 0);
199             framebuffer = framebuffers[0];
200             gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, framebuffer);
201 
202             int depthbuffer;
203             int[] renderbuffers = new int[1];
204             gl11ep.glGenRenderbuffersOES(1, renderbuffers, 0);
205             depthbuffer = renderbuffers[0];
206 
207             gl11ep.glBindRenderbufferOES(GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer);
208             gl11ep.glRenderbufferStorageOES(GL11ExtensionPack.GL_RENDERBUFFER_OES,
209                     GL11ExtensionPack.GL_DEPTH_COMPONENT16, width, height);
210             gl11ep.glFramebufferRenderbufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES,
211                     GL11ExtensionPack.GL_DEPTH_ATTACHMENT_OES,
212                     GL11ExtensionPack.GL_RENDERBUFFER_OES, depthbuffer);
213 
214             gl11ep.glFramebufferTexture2DOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES,
215                     GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES, GL10.GL_TEXTURE_2D,
216                     targetTextureId, 0);
217             int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES);
218             if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) {
219                 throw new RuntimeException("Framebuffer is not complete: " +
220                         Integer.toHexString(status));
221             }
222             gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
223             return framebuffer;
224         }
225 
checkIfContextSupportsFrameBufferObject(GL10 gl)226         private boolean checkIfContextSupportsFrameBufferObject(GL10 gl) {
227             return checkIfContextSupportsExtension(gl, "GL_OES_framebuffer_object");
228         }
229 
230         /**
231          * This is not the fastest way to check for an extension, but fine if
232          * we are only checking for a few extensions each time a context is created.
233          * @param gl
234          * @param extension
235          * @return true if the extension is present in the current context.
236          */
checkIfContextSupportsExtension(GL10 gl, String extension)237         private boolean checkIfContextSupportsExtension(GL10 gl, String extension) {
238             String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " ";
239             // The extensions string is padded with spaces between extensions, but not
240             // necessarily at the beginning or end. For simplicity, add spaces at the
241             // beginning and end of the extensions string and the extension string.
242             // This means we can avoid special-case checks for the first or last
243             // extension, as well as avoid special-case checks when an extension name
244             // is the same as the first part of another extension name.
245             return extensions.indexOf(" " + extension + " ") >= 0;
246         }
247     }
248 
checkGLError(GL gl)249     static void checkGLError(GL gl) {
250         int error = ((GL10) gl).glGetError();
251         if (error != GL10.GL_NO_ERROR) {
252             throw new RuntimeException("GLError 0x" + Integer.toHexString(error));
253         }
254     }
255 
256     @Override
onCreate(Bundle savedInstanceState)257     protected void onCreate(Bundle savedInstanceState) {
258         super.onCreate(savedInstanceState);
259 
260         // Create our surface view and set it as the content of our
261         // Activity
262         mGLSurfaceView = new GLSurfaceView(this);
263         mGLSurfaceView.setRenderer(new Renderer());
264         setContentView(mGLSurfaceView);
265     }
266 
267     @Override
onResume()268     protected void onResume() {
269         // Ideally a game should implement onResume() and onPause()
270         // to take appropriate action when the activity looses focus
271         super.onResume();
272         mGLSurfaceView.onResume();
273     }
274 
275     @Override
onPause()276     protected void onPause() {
277         // Ideally a game should implement onResume() and onPause()
278         // to take appropriate action when the activity looses focus
279         super.onPause();
280         mGLSurfaceView.onPause();
281     }
282 }
283