blob: 183a8ece165b8a9e824502d0c97d69666bf41282 [file] [log] [blame]
/*
* 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 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.drawable.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);
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];
}
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
};
}