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
17package com.example.android.apis.graphics;
18
19import javax.microedition.khronos.egl.EGLConfig;
20import javax.microedition.khronos.opengles.GL;
21import javax.microedition.khronos.opengles.GL10;
22import javax.microedition.khronos.opengles.GL11ExtensionPack;
23
24import android.app.Activity;
25import android.opengl.GLSurfaceView;
26import android.opengl.GLU;
27import android.os.Bundle;
28import 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 */
36public 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
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
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
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
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
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
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
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
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         */
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
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
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
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
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