package rajawali.renderer;
import rajawali.Camera2D;
import rajawali.filters.IPostProcessingFilter;
import rajawali.materials.AMaterial;
import rajawali.materials.TextureInfo;
import rajawali.materials.TextureManager.TextureType;
import rajawali.math.MathUtil;
import rajawali.primitives.Plane;
import rajawali.util.RajLog;
import android.opengl.GLES20;
public final class PostProcessingRenderer {
private int mFrameBufferHandle;
private int mDepthBufferHandle;
private TextureInfo mFrameBufferTexInfo;
// private TextureInfo mDepthBufferTexInfo;
private Plane mPostProcessingQuad;
private Camera2D mPostProcessingCam;
private int mTextureSize;
private RajawaliRenderer mRenderer;
private IPostProcessingFilter mFilter;
private boolean mEnabled;
private boolean mInitialized;
private int mQuadSegments = 1;
private PostProcessingQuality mQuality;
public enum PostProcessingQuality {
HIGH,
MEDIUM,
LOW,
VERY_LOW
}
public PostProcessingRenderer(RajawaliRenderer renderer) {
this(renderer, -1);
}
public PostProcessingRenderer(RajawaliRenderer renderer, int frameBufferTextureSize) {
this(renderer, frameBufferTextureSize, PostProcessingQuality.MEDIUM);
}
/**
*
* @param renderer
* @param frameBufferTextureSize MathUtil.getClosestPowerOfTwo(mTextureSize) or 1024 (default)
*/
public PostProcessingRenderer(RajawaliRenderer renderer, int frameBufferTextureSize, PostProcessingQuality quality) {
mRenderer = renderer;
mTextureSize = frameBufferTextureSize;
mQuality = quality;
}
private void create() {
int[] frameBuffers = new int[1];
GLES20.glGenFramebuffers(1, frameBuffers, 0);
mFrameBufferHandle = frameBuffers[0];
if(mTextureSize == -1) {
mTextureSize = MathUtil.getClosestPowerOfTwo(mRenderer.getViewportWidth() > mRenderer.getViewportHeight() ? mRenderer.getViewportWidth() : mRenderer.getViewportHeight());
}
if(mQuality == PostProcessingQuality.MEDIUM)
mTextureSize >>= 1;
else if(mQuality == PostProcessingQuality.LOW)
mTextureSize >>= 2;
else if(mQuality == PostProcessingQuality.VERY_LOW)
mTextureSize >>= 3;
int[] depthBuffers = new int[1];
GLES20.glGenRenderbuffers(1, depthBuffers, 0);
mDepthBufferHandle = depthBuffers[0];
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, mDepthBufferHandle);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, mTextureSize, mTextureSize);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);
mFrameBufferTexInfo = mRenderer.getTextureManager().addTexture(null, mTextureSize, mTextureSize, TextureType.FRAME_BUFFER);
mPostProcessingQuad = new Plane(1, 1, mQuadSegments, mQuadSegments);
mPostProcessingQuad.setMaterial((AMaterial)mFilter);
mPostProcessingQuad.setDoubleSided(true);
mPostProcessingQuad.setRotZ(-90);
mPostProcessingQuad.setRotY(180);
mPostProcessingCam = new Camera2D();
mPostProcessingCam.setProjectionMatrix(0, 0);
mPostProcessingQuad.addTexture(mFrameBufferTexInfo);
mInitialized = true;
}
public void setFilter(IPostProcessingFilter filter) {
mFilter = filter;
}
public void bind() {
if(!mInitialized)
create();
GLES20.glViewport(0, 0, mTextureSize, mTextureSize);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBufferHandle);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mFrameBufferTexInfo.getTextureId(), 0);
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
RajLog.d("Could not bind post processing frame buffer." + status);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
}
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, mDepthBufferHandle);
}
public void unbind() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);
GLES20.glViewport(0, 0, mRenderer.getViewportWidth(), mRenderer.getViewportHeight());
}
public void destroy() {
GLES20.glDeleteFramebuffers(1, new int[] { mFrameBufferHandle }, 0);
GLES20.glDeleteRenderbuffers(1, new int[] { mDepthBufferHandle }, 0);
if (mFrameBufferTexInfo!=null) mRenderer.getTextureManager().removeTexture(mFrameBufferTexInfo);
}
public void reload() {
if(mPostProcessingQuad != null)
mPostProcessingQuad.reload();
int[] frameBuffers = new int[1];
GLES20.glGenFramebuffers(1, frameBuffers, 0);
mFrameBufferHandle = frameBuffers[0];
int[] depthBuffers = new int[1];
GLES20.glGenRenderbuffers(1, depthBuffers, 0);
mDepthBufferHandle = depthBuffers[0];
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, mDepthBufferHandle);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, mTextureSize, mTextureSize);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);
}
public void render() {
unbind();
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDepthMask(true);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
mPostProcessingQuad.render(mPostProcessingCam, mPostProcessingCam.getProjectionMatrix(), mPostProcessingCam.getViewMatrix(), null);
}
public boolean isEnabled() {
return mEnabled;
}
public void setEnabled(boolean enabled) {
this.mEnabled = enabled;
}
public boolean isInitialized() {
return mInitialized;
}
public void setInitialized(boolean initialized) {
this.mInitialized = initialized;
}
public void checkError(String message, int status) {
StringBuffer sb = new StringBuffer();
sb.append(message).append(" - ");
switch(status)
{
case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
sb.append( "OpenGL framebuffer format not supported. ");
break;
case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
sb.append( "OpenGL framebuffer missing attachment.");
break;
case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
sb.append( "OpenGL framebuffer attached images must have same dimensions.");
break;
case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
sb.append( "OpenGL framebuffer attached images must have same format.");
break;
case GLES20.GL_INVALID_FRAMEBUFFER_OPERATION:
sb.append("OpenGL invalid framebuffer operation.");
break;
case GLES20.GL_NO_ERROR:
sb.append("No errors.");
break;
case GLES20.GL_INVALID_VALUE:
sb.append("Invalid value");
break;
case GLES20.GL_INVALID_OPERATION:
sb.append("Invalid operation");
break;
case GLES20.GL_INVALID_ENUM:
sb.append("Invalid enum");
break;
case GLES20.GL_FRAMEBUFFER_COMPLETE:
sb.append("Framebuffer complete.");
break;
default:
sb.append("OpenGL error: " + status);
break;
}
RajLog.d(sb.toString());
}
public int getQuadSegments() {
return mQuadSegments;
}
public void setQuadSegments(int quadSegments) {
this.mQuadSegments = quadSegments;
}
public PostProcessingQuality getQuality() {
return mQuality;
}
public void setQuality(PostProcessingQuality quality) {
this.mQuality = quality;
}
}