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 
GameRenderer(Context context, Game game, int gameWidth, int gameHeight)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 
onSurfaceCreated(GL10 gl, EGLConfig config)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 
hackBrokenDevices()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 
loadTextures(GL10 gl, TextureLibrary library)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 
flushTextures(GL10 gl, TextureLibrary library)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 
loadBuffers(GL10 gl, BufferLibrary library)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 
flushBuffers(GL10 gl, BufferLibrary library)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 
onSurfaceLost()170     public void onSurfaceLost() {
171         mGame.onSurfaceLost();
172     }
173 
requestCallback()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. */
onDrawFrame(GL10 gl)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 
onSurfaceChanged(GL10 gl, int w, int h)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 
setDrawQueue(ObjectManager queue, float cameraX, float cameraY)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 
onPause()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 
waitDrawingComplete()321     public synchronized void waitDrawingComplete() {
322     }
323 
setContext(Context newContext)324     public void setContext(Context newContext) {
325         mContext = newContext;
326     }
327 
328 
329 }
330