package com.android.droidgraph.renderer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashSet;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11ExtensionPack;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
import android.util.Log;
import android.view.MotionEvent;
import com.android.droidgraph.fx.FXGroup;
import com.android.droidgraph.lighting.LightStudio;
import com.android.droidgraph.primitive.Cube;
import com.android.droidgraph.primitive.Triangle;
import com.android.droidgraph.scene.SGAbstractShape;
import com.android.droidgraph.scene.SGNode;
import com.android.droidgraph.scene.SGView;
import com.android.droidgraph.util.GLH;
import com.android.droidgraph.util.PrintLogUtil;
import com.android.droidgraph.util.SGColorI;
import com.android.droidgraph.util.SGLog;
import com.android.droidgraph.util.Settings;
public class BufferedRenderer implements GLSurfaceView.Renderer {
// for debug
PrintLogUtil log = new PrintLogUtil();
String TAG = "SGRenderer";
private boolean mContextSupportsFrameBufferObject;
// private int mTargetTexture;
private int mFramebuffer;
private int mFramebufferWidth = 512;
private int mFramebufferHeight = 512;
private int mSurfaceWidth;
private int mSurfaceHeight;
private float[] background = { 0f, 0f, 0f, 1f };
private int renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY;
private FXGroup mSceneGroup;
private float lookx = 0f;
private float looky = 0f;
private LightStudio mLightStudio;
private Settings mSettings;
private static final boolean DEBUG_RENDER_OFFSCREEN_ONSCREEN = false;
Triangle triangle;
Cube cube;
public BufferedRenderer(SGView view, Settings settings, SGNode... nodes) {
mSettings = settings;
mSettings.setRenderer(this);
mLightStudio = new LightStudio(mSettings);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
checkGLError(gl);
mSurfaceWidth = width;
mSurfaceHeight = height;
gl.glViewport(0, 0, width, height); // Reset The Current Viewport
mSettings.setScreenDimensions(width, height);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig eglc) {
GLH.setGL(gl);
mSettings.setGL(gl);
mContextSupportsFrameBufferObject = checkIfContextSupportsFrameBufferObject(gl);
if (mContextSupportsFrameBufferObject) {
// mTargetTexture = createTargetTexture(gl, mFramebufferWidth, mFramebufferHeight);
mFramebuffer = createFrameBuffer(gl, mFramebufferWidth, mFramebufferHeight);
triangle = new Triangle();
cube = new Cube();
mLightStudio.load(gl);
mSceneGroup.load(gl);
}
}
@Override
public void onDrawFrame(GL10 gl) {
checkGLError(gl);
if (mContextSupportsFrameBufferObject) {
GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;
if (DEBUG_RENDER_OFFSCREEN_ONSCREEN) {
drawOffscreenImage(gl, mSurfaceWidth, mSurfaceHeight);
} else {
if(mSettings.pick()){
Log.d(TAG, "pick method called");
gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, mFramebuffer);
drawToBuffer(gl, mSurfaceWidth, mSurfaceWidth);
mSettings.pick(false);
} else {
gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
drawOnscreen(gl, mSurfaceWidth, mSurfaceHeight);
}
}
} else {
// Current context doesn't support frame buffer objects.
// Indicate this by drawing a red background.
gl.glClearColor(1, 0, 0, 0);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
}
}
// Here is where the main drawing loop which is displayed
// on the screen takes place.
private void drawOnscreen(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
gl.glClearColor(background[0], background[1], background[2], background[3]);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, lookx, looky, 5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// mLightStudio.render(gl);
mSceneGroup.render(gl);
// mLightStudio.killRender(gl);
// Restore default state so the other renderer is not affected.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
private void drawOffscreenImage(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
gl.glEnable(GL10.GL_CULL_FACE);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glClearColor(background[0], background[1], background[2], background[3]);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, lookx, looky, 5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
// gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
// Render the pick color ID here
mSceneGroup.render(gl);
// pickReadPixels(gl, 3, 3);
// Restore default state so the other renderer is not affected.
gl.glDisable(GL10.GL_CULL_FACE);
gl.glDisable(GL10.GL_DEPTH_TEST);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
// gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
gl.glDisableClientState(GL10.GL_COLOR_ARRAY);
}
private void drawToBuffer(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
float ratio = (float) width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
gl.glClearColor(background[0], background[1], background[2], background[3]);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, lookx, looky, 5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
mSceneGroup.renderColorID(gl);
// Restore default state so the other renderer is not affected.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
}
private void pickReadPixels(GL10 gl, int height, int width) {
float x = mSettings.getPickPoint()[0];
float y = mSettings.getPickPoint()[1];
int screenshotSize = width * height;
ByteBuffer bb = ByteBuffer.allocateDirect(screenshotSize * 4);
bb.order(ByteOrder.nativeOrder());
mSettings.getGL().glReadPixels((int) x, (int) y, width, height, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, bb);
int pixelsBuffer[] = new int[screenshotSize];
bb.asIntBuffer().get(pixelsBuffer);
bb = null;
StringBuilder sb = new StringBuilder();
sb.append("");
for(int i = 0; i < pixelsBuffer.length; i++) {
sb.append(", " + pixelsBuffer[i]);
}
Log.d(TAG, sb.toString());
pixelsBuffer = null;
}
public void setSceneGroup(SGNode group) {
// SGGroup sggroup = (SGGroup) group;
mSceneGroup = (FXGroup) group;
}
public void setLookAtX(float x) {
this.lookx = x;
}
public void setLookAtY(float y) {
this.looky = y;
}
public void setRenderMode(int mode) {
if (mode == 0 || renderMode == 1) {
renderMode = mode;
} else {
SGLog.error("render mode must be 0 - GLSurfaceView.RENDERMODE_CONTINUOUSLY, or 1 - GLSurfaceView.RENDERMODE_WHEN_DIRTY");
}
}
public void setContext(Context context) {
mSettings.setContext(context);
}
public LightStudio getLightStudio() {
return mLightStudio;
}
public void processSelection(MotionEvent e, SGColorI inputColor) {
HashSet<SGAbstractShape> gNodeIDMap = mSettings.getNodeIDMap();
for (SGAbstractShape node : gNodeIDMap) {
if (inputColor.equals(node.getColorID())) {
node.setSelected(true);
}
}
}
static void checkGLError(GL gl) {
int error = ((GL10) gl).glGetError();
if (error != GL10.GL_NO_ERROR) {
throw new RuntimeException("GLError 0x"
+ Integer.toHexString(error));
}
}
private boolean checkIfContextSupportsFrameBufferObject(GL10 gl) {
return checkIfContextSupportsExtension(gl, "GL_OES_framebuffer_object");
}
/**
* This is not the fastest way to check for an extension, but fine if we are
* only checking for a few extensions each time a context is created.
*
* @param gl
* @param extension
* @return true if the extension is present in the current context.
*/
private boolean checkIfContextSupportsExtension(GL10 gl, String extension) {
String extensions = " " + gl.glGetString(GL10.GL_EXTENSIONS) + " ";
// The extensions string is padded with spaces between extensions, but
// not
// necessarily at the beginning or end. For simplicity, add spaces at
// the
// beginning and end of the extensions string and the extension string.
// This means we can avoid special-case checks for the first or last
// extension, as well as avoid special-case checks when an extension
// name
// is the same as the first part of another extension name.
return extensions.indexOf(" " + extension + " ") >= 0;
}
private int createTargetTexture(GL10 gl, int width, int height) {
int texture;
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
texture = textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
gl.glTexImage2D(GL10.GL_TEXTURE_2D, 0, GL10.GL_RGBA, width, height, 0, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, null);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
return texture;
}
private int createFrameBuffer(GL10 gl, int width, int height
// int targetTextureId
) {
GL11ExtensionPack gl11ep = (GL11ExtensionPack) gl;
int framebuffer;
int[] framebuffers = new int[1];
gl11ep.glGenFramebuffersOES(1, framebuffers, 0);
framebuffer = framebuffers[0];
gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, framebuffer);
int depthbuffer;
int[] renderbuffers = new int[1];
gl11ep.glGenRenderbuffersOES(1, renderbuffers, 0);
depthbuffer = renderbuffers[0];
gl11ep.glBindRenderbufferOES(
GL11ExtensionPack.GL_RENDERBUFFER_OES,
depthbuffer
);
gl11ep.glRenderbufferStorageOES(
GL11ExtensionPack.GL_RENDERBUFFER_OES,
GL11ExtensionPack.GL_DEPTH_COMPONENT16,
width,
height
);
gl11ep.glFramebufferRenderbufferOES(
GL11ExtensionPack.GL_FRAMEBUFFER_OES,
GL11ExtensionPack.GL_DEPTH_ATTACHMENT_OES,
GL11ExtensionPack.GL_RENDERBUFFER_OES,
depthbuffer
);
// gl11ep.glFramebufferTexture2DOES(
// GL11ExtensionPack.GL_FRAMEBUFFER_OES,
// GL11ExtensionPack.GL_COLOR_ATTACHMENT0_OES,
// GL10.GL_TEXTURE_2D,
// targetTextureId,
// 0
// );
int status = gl11ep.glCheckFramebufferStatusOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES);
if (status != GL11ExtensionPack.GL_FRAMEBUFFER_COMPLETE_OES) {
throw new RuntimeException("Framebuffer is not complete: "
+ Integer.toHexString(status));
}
gl11ep.glBindFramebufferOES(GL11ExtensionPack.GL_FRAMEBUFFER_OES, 0);
return framebuffer;
}
}