1/* 2 * Copyright (C) 2011 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 18package com.android.nfc; 19 20import android.content.Context; 21import android.graphics.Bitmap; 22import android.graphics.BitmapFactory; 23import android.graphics.SurfaceTexture; 24import android.opengl.GLUtils; 25import android.util.Log; 26 27import java.io.IOException; 28import java.io.InputStream; 29import java.nio.ByteBuffer; 30import java.nio.ByteOrder; 31import java.nio.FloatBuffer; 32import java.nio.ShortBuffer; 33 34import javax.microedition.khronos.egl.EGL10; 35import javax.microedition.khronos.egl.EGLConfig; 36import javax.microedition.khronos.egl.EGLContext; 37import javax.microedition.khronos.egl.EGLDisplay; 38import javax.microedition.khronos.egl.EGLSurface; 39import javax.microedition.khronos.opengles.GL10; 40 41public class FireflyRenderThread extends Thread { 42 private static final String LOG_TAG = "NfcFireflyThread"; 43 44 SurfaceTexture mSurface; 45 46 EGL10 mEgl; 47 EGLDisplay mEglDisplay; 48 EGLConfig mEglConfig; 49 EGLContext mEglContext; 50 EGLSurface mEglSurface; 51 GL10 mGL; 52 53 static final int NUM_FIREFLIES = 200; 54 55 static final float NEAR_CLIPPING_PLANE = 50f; 56 static final float FAR_CLIPPING_PLANE = 100f; 57 58 static final int[] sEglConfig = { 59 EGL10.EGL_RED_SIZE, 8, 60 EGL10.EGL_GREEN_SIZE, 8, 61 EGL10.EGL_BLUE_SIZE, 8, 62 EGL10.EGL_ALPHA_SIZE, 0, 63 EGL10.EGL_DEPTH_SIZE, 0, 64 EGL10.EGL_STENCIL_SIZE, 0, 65 EGL10.EGL_NONE 66 }; 67 68 // Vertices for drawing a 32x32 rect 69 static final float mVertices[] = { 70 0.0f, 0.0f, 0.0f, // 0, Top Left 71 0.0f, 32.0f, 0.0f, // 1, Bottom Left 72 32.0f, 32.0f, 0.0f, // 2, Bottom Right 73 32.0f, 0.0f, 0.0f, // 3, Top Right 74 }; 75 76 // Mapping coordinates for the texture 77 static final float mTextCoords[] = { 78 0.0f, 0.0f, 79 1.0f, 0.0f, 80 1.0f, 1.0f, 81 0.0f, 1.0f 82 }; 83 84 // Connecting order (draws a square) 85 static final short[] mIndices = { 0, 1, 2, 0, 2, 3 }; 86 87 // Buffer holding the vertices 88 FloatBuffer mVertexBuffer; 89 90 // Buffer holding the indices 91 ShortBuffer mIndexBuffer; 92 93 // Buffer holding the texture mapping coordinates 94 FloatBuffer mTextureBuffer; 95 96 // Holding the handle to the texture 97 int mTextureId; 98 99 final Context mContext; 100 final int mDisplayWidth; 101 final int mDisplayHeight; 102 103 Firefly[] mFireflies; 104 long mStartTime; 105 106 // Read/written by multiple threads 107 volatile boolean mFinished; 108 volatile boolean mFadeOut; 109 110 public FireflyRenderThread(Context context, SurfaceTexture surface, int width, int height) { 111 mSurface = surface; 112 mContext = context; 113 mDisplayWidth = width; 114 mDisplayHeight = height; 115 mFinished = false; 116 } 117 118 public void finish() { 119 mFinished = true; 120 } 121 122 public void fadeOut() { 123 mFadeOut = true; 124 } 125 126 void initShapes() { 127 // First, build the vertex, texture and index buffers 128 ByteBuffer vbb = ByteBuffer.allocateDirect(mVertices.length * 4); // Float => 4 bytes 129 vbb.order(ByteOrder.nativeOrder()); 130 mVertexBuffer = vbb.asFloatBuffer(); 131 mVertexBuffer.put(mVertices); 132 mVertexBuffer.position(0); 133 134 ByteBuffer ibb = ByteBuffer.allocateDirect(mIndices.length * 2); // Short => 2 bytes 135 ibb.order(ByteOrder.nativeOrder()); 136 mIndexBuffer = ibb.asShortBuffer(); 137 mIndexBuffer.put(mIndices); 138 mIndexBuffer.position(0); 139 140 ByteBuffer tbb = ByteBuffer.allocateDirect(mTextCoords.length * 4); 141 tbb.order(ByteOrder.nativeOrder()); 142 mTextureBuffer = tbb.asFloatBuffer(); 143 mTextureBuffer.put(mTextCoords); 144 mTextureBuffer.position(0); 145 146 mFadeOut = false; 147 148 mFireflies = new Firefly[NUM_FIREFLIES]; 149 for (int i = 0; i < NUM_FIREFLIES; i++) { 150 mFireflies[i] = new Firefly(); 151 } 152 loadStarTexture(); 153 } 154 155 void loadStarTexture() { 156 int[] textureIds = new int[1]; 157 mGL.glGenTextures(1, textureIds, 0); 158 mTextureId = textureIds[0]; 159 160 InputStream in = null; 161 try { 162 // Remember that both texture dimensions must be a power of 2! 163 in = mContext.getAssets().open("star.png"); 164 165 Bitmap bitmap = BitmapFactory.decodeStream(in); 166 mGL.glBindTexture(GL10.GL_TEXTURE_2D, mTextureId); 167 168 mGL.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR); 169 mGL.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); 170 171 GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); 172 173 bitmap.recycle(); 174 175 } catch (IOException e) { 176 Log.e(LOG_TAG, "IOException opening assets."); 177 } finally { 178 if (in != null) { 179 try { 180 in.close(); 181 } catch (IOException e) { } 182 } 183 } 184 } 185 186 @Override 187 public void run() { 188 if (!initGL()) { 189 finishGL(); 190 return; 191 } 192 193 mGL.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 194 195 initShapes(); 196 197 mGL.glViewport(0, 0, mDisplayWidth, mDisplayHeight); 198 199 // make adjustments for screen ratio 200 mGL.glMatrixMode(GL10.GL_PROJECTION); 201 mGL.glLoadIdentity(); 202 mGL.glFrustumf(-mDisplayWidth, mDisplayWidth, mDisplayHeight, -mDisplayHeight, NEAR_CLIPPING_PLANE, FAR_CLIPPING_PLANE); 203 204 // Switch back to modelview 205 mGL.glMatrixMode(GL10.GL_MODELVIEW); 206 mGL.glLoadIdentity(); 207 208 mGL.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); 209 mGL.glDepthMask(true); 210 211 // Reset firefly models 212 for (Firefly firefly : mFireflies) { 213 firefly.reset(); 214 } 215 216 mStartTime = System.currentTimeMillis(); 217 218 while (!mFinished) { 219 long timeElapsedMs = System.currentTimeMillis() - mStartTime; 220 mStartTime = System.currentTimeMillis(); 221 222 checkCurrent(); 223 224 mGL.glClear(GL10.GL_COLOR_BUFFER_BIT); 225 mGL.glLoadIdentity(); 226 227 mGL.glEnable(GL10.GL_TEXTURE_2D); 228 mGL.glEnable(GL10.GL_BLEND); 229 mGL.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE); 230 231 for (Firefly firefly : mFireflies) { 232 firefly.updatePositionAndScale(timeElapsedMs); 233 firefly.draw(); 234 } 235 236 if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { 237 Log.e(LOG_TAG, "Could not swap buffers"); 238 mFinished = true; 239 } 240 241 long elapsed = System.currentTimeMillis() - mStartTime; 242 try { 243 Thread.sleep(Math.max(30 - elapsed, 0)); 244 } catch (InterruptedException e) { 245 246 } 247 } 248 finishGL(); 249 } 250 251 boolean initGL() { 252 // Initialize openGL engine 253 mEgl = (EGL10) EGLContext.getEGL(); 254 255 mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); 256 if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { 257 Log.e(LOG_TAG, "eglGetDisplay failed " + 258 GLUtils.getEGLErrorString(mEgl.eglGetError())); 259 return false; 260 } 261 262 int[] version = new int[2]; 263 if (!mEgl.eglInitialize(mEglDisplay, version)) { 264 Log.e(LOG_TAG, "eglInitialize failed " + 265 GLUtils.getEGLErrorString(mEgl.eglGetError())); 266 return false; 267 } 268 269 mEglConfig = chooseEglConfig(); 270 if (mEglConfig == null) { 271 Log.e(LOG_TAG, "eglConfig not initialized."); 272 return false; 273 } 274 275 mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, null); 276 277 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null); 278 279 if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { 280 int error = mEgl.eglGetError(); 281 Log.e(LOG_TAG,"createWindowSurface returned error"); 282 return false; 283 } 284 285 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 286 Log.e(LOG_TAG, "eglMakeCurrent failed " + 287 GLUtils.getEGLErrorString(mEgl.eglGetError())); 288 return false; 289 } 290 291 mGL = (GL10) mEglContext.getGL(); 292 293 return true; 294 } 295 296 private void finishGL() { 297 if (mEgl == null || mEglDisplay == null) { 298 // Nothing to free 299 return; 300 } 301 // Unbind the current surface and context from the display 302 mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, 303 EGL10.EGL_NO_CONTEXT); 304 if (mEglContext != null) { 305 mEgl.eglDestroyContext(mEglDisplay, mEglContext); 306 } 307 if (mEglSurface != null) { 308 mEgl.eglDestroySurface(mEglDisplay, mEglSurface); 309 } 310 } 311 312 private void checkCurrent() { 313 if (!mEglContext.equals(mEgl.eglGetCurrentContext()) || 314 !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) { 315 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { 316 throw new RuntimeException("eglMakeCurrent failed " 317 + GLUtils.getEGLErrorString(mEgl.eglGetError())); 318 } 319 } 320 } 321 322 private EGLConfig chooseEglConfig() { 323 int[] configsCount = new int[1]; 324 EGLConfig[] configs = new EGLConfig[1]; 325 if (!mEgl.eglChooseConfig(mEglDisplay, sEglConfig, configs, 1, configsCount)) { 326 throw new IllegalArgumentException("eglChooseConfig failed " + 327 GLUtils.getEGLErrorString(mEgl.eglGetError())); 328 } else if (configsCount[0] > 0) { 329 return configs[0]; 330 } 331 return null; 332 } 333 334 public class Firefly { 335 static final float TEXTURE_HEIGHT = 30f; // TODO use measurement of texture size 336 static final float SPEED = .5f; 337 338 float mX; // between -mDisplayHeight and mDisplayHeight 339 float mY; // between -mDisplayWidth and mDisplayWidth 340 float mZ; // between 0.0 (near) and 1.0 (far) 341 float mZ0; 342 float mT; 343 float mScale; 344 float mAlpha; 345 346 public Firefly() { 347 reset(); 348 } 349 350 void reset() { 351 mX = (float) (Math.random() * mDisplayWidth) * 4 - 2 * mDisplayWidth; 352 mY = (float) (Math.random() * mDisplayHeight) * 4 - 2 * mDisplayHeight; 353 mZ0 = mZ = (float) (Math.random()) * 2 - 1; 354 mT = 0f; 355 mScale = 1.5f; 356 mAlpha = 0f; 357 } 358 359 public void updatePositionAndScale(long timeElapsedMs) { 360 mT += timeElapsedMs; 361 mZ = mZ0 + mT/1000f * SPEED; 362 mAlpha = 1f-mZ; 363 if(mZ > 1.0) reset(); 364 } 365 366 public void draw() { 367 mGL.glLoadIdentity(); 368 369 // Counter clockwise winding 370 mGL.glFrontFace(GL10.GL_CCW); 371 372 mGL.glEnableClientState(GL10.GL_VERTEX_ARRAY); 373 mGL.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 374 375 mGL.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer); 376 mGL.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer); 377 378 mGL.glTranslatef(mX, mY, -NEAR_CLIPPING_PLANE-mZ*(FAR_CLIPPING_PLANE-NEAR_CLIPPING_PLANE)); 379 mGL.glColor4f(1, 1, 1, mAlpha); 380 381 // scale around center 382 mGL.glTranslatef(TEXTURE_HEIGHT/2, TEXTURE_HEIGHT/2, 0); 383 mGL.glScalef(mScale, mScale, 0); 384 mGL.glTranslatef(-TEXTURE_HEIGHT/2, -TEXTURE_HEIGHT/2, 0); 385 386 mGL.glDrawElements(GL10.GL_TRIANGLES, mIndices.length, GL10.GL_UNSIGNED_SHORT, 387 mIndexBuffer); 388 389 mGL.glColor4f(1, 1, 1, 1); 390 mGL.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); 391 mGL.glDisableClientState(GL10.GL_VERTEX_ARRAY); 392 } 393 } 394} 395