blob: 3fa1185eb2205a50093f2f82bf7b9ed14b8fe7a6 [file] [log] [blame]
/*
* Copyright (C) 2010 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.replica.replicaisland;
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import com.replica.replicaisland.RenderSystem.RenderElement;
/**
* GameRenderer the top-level rendering interface for the game engine. It is called by
* GLSurfaceView and is responsible for submitting commands to OpenGL. GameRenderer receives a
* queue of renderable objects from the thread and uses that to draw the scene every frame. If
* no queue is available then no drawing is performed. If the queue is not changed from frame to
* frame, the same scene will be redrawn every frame.
* The GameRenderer also invokes texture loads when it is activated.
*/
public class GameRenderer implements GLSurfaceView.Renderer {
private static final int PROFILE_REPORT_DELAY = 3 * 1000;
private int mWidth;
private int mHeight;
private int mHalfWidth;
private int mHalfHeight;
private float mScaleX;
private float mScaleY;
private Context mContext;
private long mLastTime;
private int mProfileFrames;
private long mProfileWaitTime;
private long mProfileFrameTime;
private long mProfileSubmitTime;
private int mProfileObjectCount;
private ObjectManager mDrawQueue;
private boolean mDrawQueueChanged;
private Game mGame;
private Object mDrawLock;
float mCameraX;
float mCameraY;
boolean mCallbackRequested;
public GameRenderer(Context context, Game game, int gameWidth, int gameHeight) {
mContext = context;
mGame = game;
mWidth = gameWidth;
mHeight = gameHeight;
mHalfWidth = gameWidth / 2;
mHalfHeight = gameHeight / 2;
mScaleX = 1.0f;
mScaleY = 1.0f;
mDrawQueueChanged = false;
mDrawLock = new Object();
mCameraX = 0.0f;
mCameraY = 0.0f;
mCallbackRequested = false;
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
/*
* 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(0.0f, 0.0f, 0.0f, 1);
gl.glShadeModel(GL10.GL_FLAT);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glEnable(GL10.GL_TEXTURE_2D);
/*
* 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.glDisable(GL10.GL_LIGHTING);
gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
String version = gl.glGetString(GL10.GL_VERSION);
String renderer = gl.glGetString(GL10.GL_RENDERER);
boolean isSoftwareRenderer = renderer.contains("PixelFlinger");
boolean isOpenGL10 = version.contains("1.0");
boolean supportsDrawTexture = extensions.contains("draw_texture");
// VBOs are standard in GLES1.1
// No use using VBOs when software renderering, esp. since older versions of the software renderer
// had a crash bug related to freeing VBOs.
boolean supportsVBOs = !isSoftwareRenderer && (!isOpenGL10 || extensions.contains("vertex_buffer_object"));
ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
params.supportsDrawTexture = supportsDrawTexture;
params.supportsVBOs = supportsVBOs;
hackBrokenDevices();
DebugLog.i("Graphics Support", version + " (" + renderer + "): " +(supportsDrawTexture ? "draw texture," : "") + (supportsVBOs ? "vbos" : ""));
mGame.onSurfaceCreated();
}
private void hackBrokenDevices() {
// Some devices are broken. Fix them here. This is pretty much the only
// device-specific code in the whole project. Ugh.
ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
if (Build.PRODUCT.contains("morrison")) {
// This is the Motorola Cliq. This device LIES and says it supports
// VBOs, which it actually does not (or, more likely, the extensions string
// is correct and the GL JNI glue is broken).
params.supportsVBOs = false;
// TODO: if Motorola fixes this, I should switch to using the fingerprint
// (blur/morrison/morrison/morrison:1.5/CUPCAKE/091007:user/ota-rel-keys,release-keys)
// instead of the product name so that newer versions use VBOs.
}
}
public void loadTextures(GL10 gl, TextureLibrary library) {
if (gl != null) {
library.loadAll(mContext, gl);
DebugLog.d("AndouKun", "Textures Loaded.");
}
}
public void flushTextures(GL10 gl, TextureLibrary library) {
if (gl != null) {
library.deleteAll(gl);
DebugLog.d("AndouKun", "Textures Unloaded.");
}
}
public void loadBuffers(GL10 gl, BufferLibrary library) {
if (gl != null) {
library.generateHardwareBuffers(gl);
DebugLog.d("AndouKun", "Buffers Created.");
}
}
public void flushBuffers(GL10 gl, BufferLibrary library) {
if (gl != null) {
library.releaseHardwareBuffers(gl);
DebugLog.d("AndouKun", "Buffers Released.");
}
}
public void onSurfaceLost() {
mGame.onSurfaceLost();
}
public void requestCallback() {
mCallbackRequested = true;
}
/** Draws the scene. Note that the draw queue is locked for the duration of this function. */
public void onDrawFrame(GL10 gl) {
long time = SystemClock.uptimeMillis();
long time_delta = (time - mLastTime);
synchronized(mDrawLock) {
if (!mDrawQueueChanged) {
while (!mDrawQueueChanged) {
try {
mDrawLock.wait();
} catch (InterruptedException e) {
// No big deal if this wait is interrupted.
}
}
}
mDrawQueueChanged = false;
}
final long wait = SystemClock.uptimeMillis();
if (mCallbackRequested) {
mGame.onSurfaceReady();
mCallbackRequested = false;
}
DrawableBitmap.beginDrawing(gl, mWidth, mHeight);
synchronized (this) {
if (mDrawQueue != null && mDrawQueue.getObjects().getCount() > 0) {
OpenGLSystem.setGL(gl);
FixedSizeArray<BaseObject> objects = mDrawQueue.getObjects();
Object[] objectArray = objects.getArray();
final int count = objects.getCount();
final float scaleX = mScaleX;
final float scaleY = mScaleY;
final float halfWidth = mHalfWidth;
final float halfHeight = mHalfHeight;
mProfileObjectCount += count;
for (int i = 0; i < count; i++) {
RenderElement element = (RenderElement)objectArray[i];
float x = element.x;
float y = element.y;
if (element.cameraRelative) {
x = (x - mCameraX) + halfWidth;
y = (y - mCameraY) + halfHeight;
}
element.mDrawable.draw(x, y, scaleX, scaleY);
}
OpenGLSystem.setGL(null);
} else if (mDrawQueue == null) {
// If we have no draw queue, clear the screen. If we have a draw queue that
// is empty, we'll leave the frame buffer alone.
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
}
}
DrawableBitmap.endDrawing(gl);
long time2 = SystemClock.uptimeMillis();
mLastTime = time2;
mProfileFrameTime += time_delta;
mProfileSubmitTime += time2 - time;
mProfileWaitTime += wait - time;
mProfileFrames++;
if (mProfileFrameTime > PROFILE_REPORT_DELAY) {
final int validFrames = mProfileFrames;
final long averageFrameTime = mProfileFrameTime / validFrames;
final long averageSubmitTime = mProfileSubmitTime / validFrames;
final float averageObjectsPerFrame = (float)mProfileObjectCount / validFrames;
final long averageWaitTime = mProfileWaitTime / validFrames;
DebugLog.d("Render Profile",
"Average Submit: " + averageSubmitTime
+ " Average Draw: " + averageFrameTime
+ " Objects/Frame: " + averageObjectsPerFrame
+ " Wait Time: " + averageWaitTime);
mProfileFrameTime = 0;
mProfileSubmitTime = 0;
mProfileFrames = 0;
mProfileObjectCount = 0;
}
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
DebugLog.d("AndouKun", "Surface Size Change: " + w + ", " + h);
//mWidth = w;0
//mHeight = h;
// ensure the same aspect ratio as the game
float scaleX = (float)w / mWidth;
float scaleY = (float)h / mHeight;
final int viewportWidth = (int)(mWidth * scaleX);
final int viewportHeight = (int)(mHeight * scaleY);
gl.glViewport(0, 0, viewportWidth, viewportHeight);
mScaleX = scaleX;
mScaleY = scaleY;
/*
* 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) mWidth / mHeight;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
mGame.onSurfaceReady();
}
public synchronized void setDrawQueue(ObjectManager queue, float cameraX, float cameraY) {
mDrawQueue = queue;
mCameraX = cameraX;
mCameraY = cameraY;
synchronized(mDrawLock) {
mDrawQueueChanged = true;
mDrawLock.notify();
}
}
public synchronized void onPause() {
// Stop waiting to avoid deadlock.
// TODO: this is a hack. Probably this renderer
// should just use GLSurfaceView's non-continuious render
// mode.
synchronized(mDrawLock) {
mDrawQueueChanged = true;
mDrawLock.notify();
}
}
/**
* This function blocks while drawFrame() is in progress, and may be used by other threads to
* determine when drawing is occurring.
*/
public synchronized void waitDrawingComplete() {
}
public void setContext(Context newContext) {
mContext = newContext;
}
}