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 17 package com.example.android.apis.graphics.spritetext; 18 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.nio.ByteBuffer; 22 import java.nio.ByteOrder; 23 import java.nio.FloatBuffer; 24 import java.nio.ShortBuffer; 25 26 import javax.microedition.khronos.egl.EGLConfig; 27 import javax.microedition.khronos.opengles.GL10; 28 29 import android.content.Context; 30 import android.graphics.Bitmap; 31 import android.graphics.BitmapFactory; 32 import android.graphics.Paint; 33 import android.opengl.GLSurfaceView; 34 import android.opengl.GLU; 35 import android.opengl.GLUtils; 36 import android.os.SystemClock; 37 import android.util.Log; 38 39 import com.example.android.apis.R; 40 41 public class SpriteTextRenderer implements GLSurfaceView.Renderer{ 42 SpriteTextRenderer(Context context)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 onSurfaceCreated(GL10 gl, EGLConfig config)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 onDrawFrame(GL10 gl)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 drawMsPF(GL10 gl, float rightMargin)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 drawLabel(GL10 gl, int triangleVertex, int labelId)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 onSurfaceChanged(GL10 gl, int w, int h)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 281 class Triangle { 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 draw(GL10 gl)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 getX(int vertex)334 public float getX(int vertex) { 335 return sCoords[3*vertex]; 336 } 337 getY(int vertex)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