Home | History | Annotate | Download | only in replicaisland
      1 /*
      2  * Copyright (C) 2010 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.replica.replicaisland;
     18 
     19 import android.content.Context;
     20 import android.os.Build;
     21 import android.os.SystemClock;
     22 
     23 import javax.microedition.khronos.egl.EGLConfig;
     24 import javax.microedition.khronos.opengles.GL10;
     25 
     26 import com.replica.replicaisland.RenderSystem.RenderElement;
     27 
     28 /**
     29  * GameRenderer the top-level rendering interface for the game engine.  It is called by
     30  * GLSurfaceView and is responsible for submitting commands to OpenGL.  GameRenderer receives a
     31  * queue of renderable objects from the thread and uses that to draw the scene every frame.  If
     32  * no queue is available then no drawing is performed.  If the queue is not changed from frame to
     33  * frame, the same scene will be redrawn every frame.
     34  * The GameRenderer also invokes texture loads when it is activated.
     35  */
     36 public class GameRenderer implements GLSurfaceView.Renderer {
     37     private static final int PROFILE_REPORT_DELAY = 3 * 1000;
     38 
     39     private int mWidth;
     40     private int mHeight;
     41     private int mHalfWidth;
     42     private int mHalfHeight;
     43 
     44     private float mScaleX;
     45     private float mScaleY;
     46     private Context mContext;
     47     private long mLastTime;
     48     private int mProfileFrames;
     49     private long mProfileWaitTime;
     50     private long mProfileFrameTime;
     51     private long mProfileSubmitTime;
     52     private int mProfileObjectCount;
     53 
     54     private ObjectManager mDrawQueue;
     55     private boolean mDrawQueueChanged;
     56     private Game mGame;
     57     private Object mDrawLock;
     58 
     59     float mCameraX;
     60     float mCameraY;
     61 
     62     boolean mCallbackRequested;
     63 
     64     public GameRenderer(Context context, Game game, int gameWidth, int gameHeight) {
     65         mContext = context;
     66         mGame = game;
     67         mWidth = gameWidth;
     68         mHeight = gameHeight;
     69         mHalfWidth = gameWidth / 2;
     70         mHalfHeight = gameHeight / 2;
     71         mScaleX = 1.0f;
     72         mScaleY = 1.0f;
     73         mDrawQueueChanged = false;
     74         mDrawLock = new Object();
     75         mCameraX = 0.0f;
     76         mCameraY = 0.0f;
     77         mCallbackRequested = false;
     78     }
     79 
     80     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
     81         /*
     82          * Some one-time OpenGL initialization can be made here probably based
     83          * on features of this particular context
     84          */
     85         gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
     86 
     87         gl.glClearColor(0.0f, 0.0f, 0.0f, 1);
     88         gl.glShadeModel(GL10.GL_FLAT);
     89         gl.glDisable(GL10.GL_DEPTH_TEST);
     90         gl.glEnable(GL10.GL_TEXTURE_2D);
     91         /*
     92          * By default, OpenGL enables features that improve quality but reduce
     93          * performance. One might want to tweak that especially on software
     94          * renderer.
     95          */
     96         gl.glDisable(GL10.GL_DITHER);
     97         gl.glDisable(GL10.GL_LIGHTING);
     98 
     99         gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
    100 
    101         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    102 
    103         String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
    104         String version = gl.glGetString(GL10.GL_VERSION);
    105         String renderer = gl.glGetString(GL10.GL_RENDERER);
    106         boolean isSoftwareRenderer = renderer.contains("PixelFlinger");
    107         boolean isOpenGL10 = version.contains("1.0");
    108         boolean supportsDrawTexture = extensions.contains("draw_texture");
    109         // VBOs are standard in GLES1.1
    110         // No use using VBOs when software renderering, esp. since older versions of the software renderer
    111         // had a crash bug related to freeing VBOs.
    112         boolean supportsVBOs = !isSoftwareRenderer && (!isOpenGL10 || extensions.contains("vertex_buffer_object"));
    113         ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
    114         params.supportsDrawTexture = supportsDrawTexture;
    115         params.supportsVBOs = supportsVBOs;
    116 
    117         hackBrokenDevices();
    118 
    119         DebugLog.i("Graphics Support", version + " (" + renderer + "): " +(supportsDrawTexture ?  "draw texture," : "") + (supportsVBOs ? "vbos" : ""));
    120 
    121         mGame.onSurfaceCreated();
    122 
    123     }
    124 
    125     private void hackBrokenDevices() {
    126     	// Some devices are broken.  Fix them here.  This is pretty much the only
    127     	// device-specific code in the whole project.  Ugh.
    128         ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
    129 
    130 
    131     	if (Build.PRODUCT.contains("morrison")) {
    132     		// This is the Motorola Cliq.  This device LIES and says it supports
    133     		// VBOs, which it actually does not (or, more likely, the extensions string
    134     		// is correct and the GL JNI glue is broken).
    135     		params.supportsVBOs = false;
    136     		// TODO: if Motorola fixes this, I should switch to using the fingerprint
    137     		// (blur/morrison/morrison/morrison:1.5/CUPCAKE/091007:user/ota-rel-keys,release-keys)
    138     		// instead of the product name so that newer versions use VBOs.
    139     	}
    140     }
    141 
    142     public void loadTextures(GL10 gl, TextureLibrary library) {
    143         if (gl != null) {
    144             library.loadAll(mContext, gl);
    145             DebugLog.d("AndouKun", "Textures Loaded.");
    146         }
    147     }
    148 
    149     public void flushTextures(GL10 gl, TextureLibrary library) {
    150         if (gl != null) {
    151             library.deleteAll(gl);
    152             DebugLog.d("AndouKun", "Textures Unloaded.");
    153         }
    154     }
    155 
    156     public void loadBuffers(GL10 gl, BufferLibrary library) {
    157         if (gl != null) {
    158             library.generateHardwareBuffers(gl);
    159             DebugLog.d("AndouKun", "Buffers Created.");
    160         }
    161     }
    162 
    163     public void flushBuffers(GL10 gl, BufferLibrary library) {
    164         if (gl != null) {
    165             library.releaseHardwareBuffers(gl);
    166             DebugLog.d("AndouKun", "Buffers Released.");
    167         }
    168     }
    169 
    170     public void onSurfaceLost() {
    171         mGame.onSurfaceLost();
    172     }
    173 
    174     public void requestCallback() {
    175     	mCallbackRequested = true;
    176     }
    177 
    178     /** Draws the scene.  Note that the draw queue is locked for the duration of this function. */
    179     public void onDrawFrame(GL10 gl) {
    180 
    181         long time = SystemClock.uptimeMillis();
    182         long time_delta = (time - mLastTime);
    183 
    184         synchronized(mDrawLock) {
    185             if (!mDrawQueueChanged) {
    186                 while (!mDrawQueueChanged) {
    187                     try {
    188                     	mDrawLock.wait();
    189                     } catch (InterruptedException e) {
    190                         // No big deal if this wait is interrupted.
    191                     }
    192                 }
    193             }
    194             mDrawQueueChanged = false;
    195         }
    196 
    197         final long wait = SystemClock.uptimeMillis();
    198 
    199         if (mCallbackRequested) {
    200         	mGame.onSurfaceReady();
    201         	mCallbackRequested = false;
    202         }
    203 
    204         DrawableBitmap.beginDrawing(gl, mWidth, mHeight);
    205 
    206         synchronized (this) {
    207             if (mDrawQueue != null && mDrawQueue.getObjects().getCount() > 0) {
    208                 OpenGLSystem.setGL(gl);
    209                 FixedSizeArray<BaseObject> objects = mDrawQueue.getObjects();
    210                 Object[] objectArray = objects.getArray();
    211                 final int count = objects.getCount();
    212                 final float scaleX = mScaleX;
    213                 final float scaleY = mScaleY;
    214                 final float halfWidth = mHalfWidth;
    215                 final float halfHeight = mHalfHeight;
    216                 mProfileObjectCount += count;
    217                 for (int i = 0; i < count; i++) {
    218                     RenderElement element = (RenderElement)objectArray[i];
    219                     float x = element.x;
    220                     float y = element.y;
    221                     if (element.cameraRelative) {
    222                     	x = (x - mCameraX) + halfWidth;
    223                     	y = (y - mCameraY) + halfHeight;
    224                     }
    225                     element.mDrawable.draw(x, y, scaleX, scaleY);
    226                 }
    227                 OpenGLSystem.setGL(null);
    228             } else if (mDrawQueue == null) {
    229                 // If we have no draw queue, clear the screen.  If we have a draw queue that
    230                 // is empty, we'll leave the frame buffer alone.
    231                 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    232             }
    233         }
    234 
    235         DrawableBitmap.endDrawing(gl);
    236 
    237         long time2 = SystemClock.uptimeMillis();
    238         mLastTime = time2;
    239 
    240         mProfileFrameTime += time_delta;
    241         mProfileSubmitTime += time2 - time;
    242         mProfileWaitTime += wait - time;
    243 
    244         mProfileFrames++;
    245         if (mProfileFrameTime > PROFILE_REPORT_DELAY) {
    246         	final int validFrames = mProfileFrames;
    247             final long averageFrameTime = mProfileFrameTime / validFrames;
    248             final long averageSubmitTime = mProfileSubmitTime / validFrames;
    249             final float averageObjectsPerFrame = (float)mProfileObjectCount / validFrames;
    250             final long averageWaitTime = mProfileWaitTime / validFrames;
    251 
    252             DebugLog.d("Render Profile",
    253             		"Average Submit: " + averageSubmitTime
    254             		+ "  Average Draw: " + averageFrameTime
    255             		+ " Objects/Frame: " + averageObjectsPerFrame
    256             		+ " Wait Time: " + averageWaitTime);
    257 
    258             mProfileFrameTime = 0;
    259             mProfileSubmitTime = 0;
    260             mProfileFrames = 0;
    261             mProfileObjectCount = 0;
    262         }
    263 
    264     }
    265 
    266     public void onSurfaceChanged(GL10 gl, int w, int h) {
    267         DebugLog.d("AndouKun", "Surface Size Change: " + w + ", " + h);
    268 
    269         //mWidth = w;0
    270         //mHeight = h;
    271     	// ensure the same aspect ratio as the game
    272     	float scaleX = (float)w / mWidth;
    273     	float scaleY =  (float)h / mHeight;
    274     	final int viewportWidth = (int)(mWidth * scaleX);
    275     	final int viewportHeight = (int)(mHeight * scaleY);
    276         gl.glViewport(0, 0, viewportWidth, viewportHeight);
    277         mScaleX = scaleX;
    278         mScaleY = scaleY;
    279 
    280 
    281         /*
    282          * Set our projection matrix. This doesn't have to be done each time we
    283          * draw, but usually a new projection needs to be set when the viewport
    284          * is resized.
    285          */
    286         float ratio = (float) mWidth / mHeight;
    287         gl.glMatrixMode(GL10.GL_PROJECTION);
    288         gl.glLoadIdentity();
    289         gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
    290 
    291 
    292         mGame.onSurfaceReady();
    293     }
    294 
    295     public synchronized void setDrawQueue(ObjectManager queue, float cameraX, float cameraY) {
    296 		mDrawQueue = queue;
    297 		mCameraX = cameraX;
    298 		mCameraY = cameraY;
    299     	synchronized(mDrawLock) {
    300     		mDrawQueueChanged = true;
    301     		mDrawLock.notify();
    302     	}
    303     }
    304 
    305     public synchronized void onPause() {
    306     	// Stop waiting to avoid deadlock.
    307     	// TODO: this is a hack.  Probably this renderer
    308     	// should just use GLSurfaceView's non-continuious render
    309     	// mode.
    310     	synchronized(mDrawLock) {
    311     		mDrawQueueChanged = true;
    312     		mDrawLock.notify();
    313     	}
    314     }
    315 
    316     /**
    317      * This function blocks while drawFrame() is in progress, and may be used by other threads to
    318      * determine when drawing is occurring.
    319      */
    320 
    321     public synchronized void waitDrawingComplete() {
    322     }
    323 
    324     public void setContext(Context newContext) {
    325         mContext = newContext;
    326     }
    327 
    328 
    329 }
    330