1/*
2 * Copyright (C) 2008 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.spritetext;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.nio.ByteBuffer;
22import java.nio.ByteOrder;
23import java.nio.FloatBuffer;
24import java.nio.ShortBuffer;
25
26import javax.microedition.khronos.egl.EGLConfig;
27import javax.microedition.khronos.opengles.GL10;
28
29import android.content.Context;
30import android.graphics.Bitmap;
31import android.graphics.BitmapFactory;
32import android.graphics.Paint;
33import android.opengl.GLSurfaceView;
34import android.opengl.GLU;
35import android.opengl.GLUtils;
36import android.os.SystemClock;
37import android.util.Log;
38
39import com.example.android.apis.R;
40
41public class SpriteTextRenderer implements GLSurfaceView.Renderer{
42
43    public SpriteTextRenderer(Context context) {
44        mContext = context;
45        mTriangle = new Triangle();
46        mProjector = new Projector();
47        mLabelPaint = new Paint();
48        mLabelPaint.setTextSize(32);
49        mLabelPaint.setAntiAlias(true);
50        mLabelPaint.setARGB(0xff, 0x00, 0x00, 0x00);
51    }
52
53    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
54        /*
55         * By default, OpenGL enables features that improve quality
56         * but reduce performance. One might want to tweak that
57         * especially on software renderer.
58         */
59        gl.glDisable(GL10.GL_DITHER);
60
61        /*
62         * Some one-time OpenGL initialization can be made here
63         * probably based on features of this particular context
64         */
65        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
66                GL10.GL_FASTEST);
67
68        gl.glClearColor(.5f, .5f, .5f, 1);
69        gl.glShadeModel(GL10.GL_SMOOTH);
70        gl.glEnable(GL10.GL_DEPTH_TEST);
71        gl.glEnable(GL10.GL_TEXTURE_2D);
72
73        /*
74         * Create our texture. This has to be done each time the
75         * surface is created.
76         */
77
78        int[] textures = new int[1];
79        gl.glGenTextures(1, textures, 0);
80
81        mTextureID = textures[0];
82        gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
83
84        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
85                GL10.GL_NEAREST);
86        gl.glTexParameterf(GL10.GL_TEXTURE_2D,
87                GL10.GL_TEXTURE_MAG_FILTER,
88                GL10.GL_LINEAR);
89
90        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
91                GL10.GL_CLAMP_TO_EDGE);
92        gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
93                GL10.GL_CLAMP_TO_EDGE);
94
95        gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
96                GL10.GL_REPLACE);
97
98        InputStream is = mContext.getResources()
99                .openRawResource(R.raw.robot);
100        Bitmap bitmap;
101        try {
102            bitmap = BitmapFactory.decodeStream(is);
103        } finally {
104            try {
105                is.close();
106            } catch(IOException e) {
107                // Ignore.
108            }
109        }
110
111        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
112        bitmap.recycle();
113
114        if (mLabels != null) {
115            mLabels.shutdown(gl);
116        } else {
117            mLabels = new LabelMaker(true, 256, 64);
118        }
119        mLabels.initialize(gl);
120        mLabels.beginAdding(gl);
121        mLabelA = mLabels.add(gl, "A", mLabelPaint);
122        mLabelB = mLabels.add(gl, "B", mLabelPaint);
123        mLabelC = mLabels.add(gl, "C", mLabelPaint);
124        mLabelMsPF = mLabels.add(gl, "ms/f", mLabelPaint);
125        mLabels.endAdding(gl);
126
127        if (mNumericSprite != null) {
128            mNumericSprite.shutdown(gl);
129        } else {
130            mNumericSprite = new NumericSprite();
131        }
132        mNumericSprite.initialize(gl, mLabelPaint);
133    }
134
135    public void onDrawFrame(GL10 gl) {
136        /*
137         * By default, OpenGL enables features that improve quality
138         * but reduce performance. One might want to tweak that
139         * especially on software renderer.
140         */
141        gl.glDisable(GL10.GL_DITHER);
142
143        gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
144                GL10.GL_MODULATE);
145
146        /*
147         * Usually, the first thing one might want to do is to clear
148         * the screen. The most efficient way of doing this is to use
149         * glClear().
150         */
151
152        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
153
154        /*
155         * Now we're ready to draw some 3D objects
156         */
157
158        gl.glMatrixMode(GL10.GL_MODELVIEW);
159        gl.glLoadIdentity();
160
161        GLU.gluLookAt(gl, 0.0f, 0.0f, -2.5f,
162                0.0f, 0.0f, 0.0f,
163                0.0f, 1.0f, 0.0f);
164
165        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
166        gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
167
168        gl.glActiveTexture(GL10.GL_TEXTURE0);
169        gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
170        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
171                GL10.GL_REPEAT);
172        gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
173                GL10.GL_REPEAT);
174
175        if (false) {
176            long time = SystemClock.uptimeMillis();
177            if (mLastTime != 0) {
178                long delta = time - mLastTime;
179                Log.w("time", Long.toString(delta));
180            }
181            mLastTime = time;
182        }
183
184        long time = SystemClock.uptimeMillis() % 4000L;
185        float angle = 0.090f * ((int) time);
186
187        gl.glRotatef(angle, 0, 0, 1.0f);
188        gl.glScalef(2.0f, 2.0f, 2.0f);
189
190        mTriangle.draw(gl);
191
192        mProjector.getCurrentModelView(gl);
193        mLabels.beginDrawing(gl, mWidth, mHeight);
194        drawLabel(gl, 0, mLabelA);
195        drawLabel(gl, 1, mLabelB);
196        drawLabel(gl, 2, mLabelC);
197        float msPFX = mWidth - mLabels.getWidth(mLabelMsPF) - 1;
198        mLabels.draw(gl, msPFX, 0, mLabelMsPF);
199        mLabels.endDrawing(gl);
200
201        drawMsPF(gl, msPFX);
202    }
203
204    private void drawMsPF(GL10 gl, float rightMargin) {
205        long time = SystemClock.uptimeMillis();
206        if (mStartTime == 0) {
207            mStartTime = time;
208        }
209        if (mFrames++ == SAMPLE_PERIOD_FRAMES) {
210            mFrames = 0;
211            long delta = time - mStartTime;
212            mStartTime = time;
213            mMsPerFrame = (int) (delta * SAMPLE_FACTOR);
214        }
215        if (mMsPerFrame > 0) {
216            mNumericSprite.setValue(mMsPerFrame);
217            float numWidth = mNumericSprite.width();
218            float x = rightMargin - numWidth;
219            mNumericSprite.draw(gl, x, 0, mWidth, mHeight);
220        }
221    }
222
223    private void drawLabel(GL10 gl, int triangleVertex, int labelId) {
224        float x = mTriangle.getX(triangleVertex);
225        float y = mTriangle.getY(triangleVertex);
226        mScratch[0] = x;
227        mScratch[1] = y;
228        mScratch[2] = 0.0f;
229        mScratch[3] = 1.0f;
230        mProjector.project(mScratch, 0, mScratch, 4);
231        float sx = mScratch[4];
232        float sy = mScratch[5];
233        float height = mLabels.getHeight(labelId);
234        float width = mLabels.getWidth(labelId);
235        float tx = sx - width * 0.5f;
236        float ty = sy - height * 0.5f;
237        mLabels.draw(gl, tx, ty, labelId);
238    }
239
240    public void onSurfaceChanged(GL10 gl, int w, int h) {
241        mWidth = w;
242        mHeight = h;
243        gl.glViewport(0, 0, w, h);
244        mProjector.setCurrentView(0, 0, w, h);
245
246        /*
247        * Set our projection matrix. This doesn't have to be done
248        * each time we draw, but usually a new projection needs to
249        * be set when the viewport is resized.
250        */
251
252        float ratio = (float) w / h;
253        gl.glMatrixMode(GL10.GL_PROJECTION);
254        gl.glLoadIdentity();
255        gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
256        mProjector.getCurrentProjection(gl);
257    }
258
259    private int mWidth;
260    private int mHeight;
261    private Context mContext;
262    private Triangle mTriangle;
263    private int mTextureID;
264    private int mFrames;
265    private int mMsPerFrame;
266    private final static int SAMPLE_PERIOD_FRAMES = 12;
267    private final static float SAMPLE_FACTOR = 1.0f / SAMPLE_PERIOD_FRAMES;
268    private long mStartTime;
269    private LabelMaker mLabels;
270    private Paint mLabelPaint;
271    private int mLabelA;
272    private int mLabelB;
273    private int mLabelC;
274    private int mLabelMsPF;
275    private Projector mProjector;
276    private NumericSprite mNumericSprite;
277    private float[] mScratch = new float[8];
278    private long mLastTime;
279}
280
281class Triangle {
282    public Triangle() {
283
284        // Buffers to be passed to gl*Pointer() functions
285        // must be direct, i.e., they must be placed on the
286        // native heap where the garbage collector cannot
287        // move them.
288        //
289        // Buffers with multi-byte datatypes (e.g., short, int, float)
290        // must have their byte order set to native order
291
292        ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
293        vbb.order(ByteOrder.nativeOrder());
294        mFVertexBuffer = vbb.asFloatBuffer();
295
296        ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
297        tbb.order(ByteOrder.nativeOrder());
298        mTexBuffer = tbb.asFloatBuffer();
299
300        ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
301        ibb.order(ByteOrder.nativeOrder());
302        mIndexBuffer = ibb.asShortBuffer();
303
304        for (int i = 0; i < VERTS; i++) {
305            for(int j = 0; j < 3; j++) {
306                mFVertexBuffer.put(sCoords[i*3+j]);
307            }
308        }
309
310        for (int i = 0; i < VERTS; i++) {
311            for(int j = 0; j < 2; j++) {
312                mTexBuffer.put(sCoords[i*3+j] * 2.0f + 0.5f);
313            }
314        }
315
316        for(int i = 0; i < VERTS; i++) {
317            mIndexBuffer.put((short) i);
318        }
319
320        mFVertexBuffer.position(0);
321        mTexBuffer.position(0);
322        mIndexBuffer.position(0);
323    }
324
325    public void draw(GL10 gl) {
326        gl.glFrontFace(GL10.GL_CCW);
327        gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
328        gl.glEnable(GL10.GL_TEXTURE_2D);
329        gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer);
330        gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, VERTS,
331                GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
332    }
333
334    public float getX(int vertex) {
335        return sCoords[3*vertex];
336    }
337
338    public float getY(int vertex) {
339        return sCoords[3*vertex+1];
340    }
341
342    private final static int VERTS = 3;
343
344    private FloatBuffer mFVertexBuffer;
345    private FloatBuffer mTexBuffer;
346    private ShortBuffer mIndexBuffer;
347    // A unit-sided equalateral triangle centered on the origin.
348    private final static float[] sCoords = {
349            // X, Y, Z
350            -0.5f, -0.25f, 0,
351             0.5f, -0.25f, 0,
352             0.0f,  0.559016994f, 0
353    };
354}
355