/* * Copyright (C) 2008 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.spritetext; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Paint; import android.opengl.GLSurfaceView; import android.opengl.GLU; import android.opengl.GLUtils; import android.os.SystemClock; import android.util.Log; import com.example.android.apis.R; public class SpriteTextRenderer implements GLSurfaceView.Renderer{ public SpriteTextRenderer(Context context) { mContext = context; mTriangle = new Triangle(); mProjector = new Projector(); mLabelPaint = new Paint(); mLabelPaint.setTextSize(32); mLabelPaint.setAntiAlias(true); mLabelPaint.setARGB(0xff, 0x00, 0x00, 0x00); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { /* * By default, OpenGL enables features that improve quality * but reduce performance. One might want to tweak that * especially on software renderer. */ gl.glDisable(GL10.GL_DITHER); /* * Some one-time OpenGL initialization can be made here * probably based on features of this particular context */ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glClearColor(.5f, .5f, .5f, 1); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glEnable(GL10.GL_TEXTURE_2D); /* * Create our texture. This has to be done each time the * surface is created. */ int[] textures = new int[1]; gl.glGenTextures(1, textures, 0); mTextureID = textures[0]; gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); 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.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE); InputStream is = mContext.getResources() .openRawResource(R.raw.robot); Bitmap bitmap; try { bitmap = BitmapFactory.decodeStream(is); } finally { try { is.close(); } catch(IOException e) { // Ignore. } } GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); if (mLabels != null) { mLabels.shutdown(gl); } else { mLabels = new LabelMaker(true, 256, 64); } mLabels.initialize(gl); mLabels.beginAdding(gl); mLabelA = mLabels.add(gl, "A", mLabelPaint); mLabelB = mLabels.add(gl, "B", mLabelPaint); mLabelC = mLabels.add(gl, "C", mLabelPaint); mLabelMsPF = mLabels.add(gl, "ms/f", mLabelPaint); mLabels.endAdding(gl); if (mNumericSprite != null) { mNumericSprite.shutdown(gl); } else { mNumericSprite = new NumericSprite(); } mNumericSprite.initialize(gl, mLabelPaint); } public void onDrawFrame(GL10 gl) { /* * By default, OpenGL enables features that improve quality * but reduce performance. One might want to tweak that * especially on software renderer. */ gl.glDisable(GL10.GL_DITHER); gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); /* * Usually, the first thing one might want to do is to clear * the screen. The most efficient way of doing this is to use * glClear(). */ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); /* * Now we're ready to draw some 3D objects */ gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); GLU.gluLookAt(gl, 0.0f, 0.0f, -2.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glActiveTexture(GL10.GL_TEXTURE0); gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID); 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); if (false) { long time = SystemClock.uptimeMillis(); if (mLastTime != 0) { long delta = time - mLastTime; Log.w("time", Long.toString(delta)); } mLastTime = time; } long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); gl.glRotatef(angle, 0, 0, 1.0f); gl.glScalef(2.0f, 2.0f, 2.0f); mTriangle.draw(gl); mProjector.getCurrentModelView(gl); mLabels.beginDrawing(gl, mWidth, mHeight); drawLabel(gl, 0, mLabelA); drawLabel(gl, 1, mLabelB); drawLabel(gl, 2, mLabelC); float msPFX = mWidth - mLabels.getWidth(mLabelMsPF) - 1; mLabels.draw(gl, msPFX, 0, mLabelMsPF); mLabels.endDrawing(gl); drawMsPF(gl, msPFX); } private void drawMsPF(GL10 gl, float rightMargin) { long time = SystemClock.uptimeMillis(); if (mStartTime == 0) { mStartTime = time; } if (mFrames++ == SAMPLE_PERIOD_FRAMES) { mFrames = 0; long delta = time - mStartTime; mStartTime = time; mMsPerFrame = (int) (delta * SAMPLE_FACTOR); } if (mMsPerFrame > 0) { mNumericSprite.setValue(mMsPerFrame); float numWidth = mNumericSprite.width(); float x = rightMargin - numWidth; mNumericSprite.draw(gl, x, 0, mWidth, mHeight); } } private void drawLabel(GL10 gl, int triangleVertex, int labelId) { float x = mTriangle.getX(triangleVertex); float y = mTriangle.getY(triangleVertex); mScratch[0] = x; mScratch[1] = y; mScratch[2] = 0.0f; mScratch[3] = 1.0f; mProjector.project(mScratch, 0, mScratch, 4); float sx = mScratch[4]; float sy = mScratch[5]; float height = mLabels.getHeight(labelId); float width = mLabels.getWidth(labelId); float tx = sx - width * 0.5f; float ty = sy - height * 0.5f; mLabels.draw(gl, tx, ty, labelId); } public void onSurfaceChanged(GL10 gl, int w, int h) { mWidth = w; mHeight = h; gl.glViewport(0, 0, w, h); mProjector.setCurrentView(0, 0, w, h); /* * Set our projection matrix. This doesn't have to be done * each time we draw, but usually a new projection needs to * be set when the viewport is resized. */ float ratio = (float) w / h; gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); mProjector.getCurrentProjection(gl); } private int mWidth; private int mHeight; private Context mContext; private Triangle mTriangle; private int mTextureID; private int mFrames; private int mMsPerFrame; private final static int SAMPLE_PERIOD_FRAMES = 12; private final static float SAMPLE_FACTOR = 1.0f / SAMPLE_PERIOD_FRAMES; private long mStartTime; private LabelMaker mLabels; private Paint mLabelPaint; private int mLabelA; private int mLabelB; private int mLabelC; private int mLabelMsPF; private Projector mProjector; private NumericSprite mNumericSprite; private float[] mScratch = new float[8]; private long mLastTime; } class Triangle { public Triangle() { // Buffers to be passed to gl*Pointer() functions // must be direct, i.e., they must be placed on the // native heap where the garbage collector cannot // move them. // // Buffers with multi-byte datatypes (e.g., short, int, float) // must have their byte order set to native order ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4); vbb.order(ByteOrder.nativeOrder()); mFVertexBuffer = vbb.asFloatBuffer(); ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4); tbb.order(ByteOrder.nativeOrder()); mTexBuffer = tbb.asFloatBuffer(); ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2); ibb.order(ByteOrder.nativeOrder()); mIndexBuffer = ibb.asShortBuffer(); for (int i = 0; i < VERTS; i++) { for(int j = 0; j < 3; j++) { mFVertexBuffer.put(sCoords[i*3+j]); } } for (int i = 0; i < VERTS; i++) { for(int j = 0; j < 2; j++) { mTexBuffer.put(sCoords[i*3+j] * 2.0f + 0.5f); } } for(int i = 0; i < VERTS; i++) { mIndexBuffer.put((short) i); } mFVertexBuffer.position(0); mTexBuffer.position(0); mIndexBuffer.position(0); } public void draw(GL10 gl) { gl.glFrontFace(GL10.GL_CCW); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer); gl.glEnable(GL10.GL_TEXTURE_2D); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexBuffer); gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, VERTS, GL10.GL_UNSIGNED_SHORT, mIndexBuffer); } public float getX(int vertex) { return sCoords[3*vertex]; } public float getY(int vertex) { return sCoords[3*vertex+1]; } private final static int VERTS = 3; private FloatBuffer mFVertexBuffer; private FloatBuffer mTexBuffer; private ShortBuffer mIndexBuffer; // A unit-sided equalateral triangle centered on the origin. private final static float[] sCoords = { // X, Y, Z -0.5f, -0.25f, 0, 0.5f, -0.25f, 0, 0.0f, 0.559016994f, 0 }; }