package demos.dualDepthPeeling;

import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.FloatBuffer;

import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.glu.GLU;

import com.jogamp.opengl.GLCapabilities;
import com.jogamp.opengl.GLEventListener;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.util.*;
import com.jogamp.opengl.util.glsl.ShaderCode;
import com.jogamp.opengl.util.glsl.ShaderProgram;
import com.jogamp.opengl.util.glsl.ShaderState;

import demos.data.DemosDataAnchor;

// Translated from C++ Version see below:
//--------------------------------------------------------------------------------------
// Order Independent Transparency with Dual Depth Peeling
//
// Author: Louis Bavoil
// Email: sdkfeedback@nvidia.com
//
// Depth peeling is traditionally used to perform order independent transparency (OIT)
// with N geometry passes for N transparency layers. Dual depth peeling enables peeling
// N transparency layers in N/2+1 passes, by peeling from the front and the back
// simultaneously using a min-max depth buffer. This sample performs either normal or
// dual depth peeling and blends on the fly.
//
// Copyright (c) NVIDIA Corporation. All rights reserved.
//--------------------------------------------------------------------------------------


public class DualDepthPeeling implements GLEventListener, KeyListener, MouseListener, MouseMotionListener
{
	public final static int DUAL_PEELING_MODE = 0;
	public final static int F2B_PEELING_MODE = 1;
	public final static int WEIGHTED_AVERAGE_MODE = 2;
	public final static int WEIGHTED_SUM_MODE = 3;

	public final static float FOVY = 30.0f;
	public final static float ZNEAR = 0.0001f;
	public final static float ZFAR = 10.0f;
	public final static float FPS_TIME_WINDOW = 1;
	public final static float MAX_DEPTH = 1.0f;

	public int g_numPasses = 4;
	public int g_imageWidth = 1024;
	public int g_imageHeight = 768;

	public Model g_model;
	public int g_quadDisplayList;
	public int[] g_vboId = new int[1];
	public int[] g_eboId = new int[1];

	public boolean g_useOQ = true;
	public int[] g_queryId = new int[1];

	public String MODEL_FILENAME = "models/dragon.obj";

	public static final String s_FrontBlenderTex = "FrontBlenderTex";	
	public static final String s_BackBlenderTex  = "BackBlenderTex";
	public static final String s_DepthBlenderTex = "DepthBlenderTex";	
	public static final String s_ColorTex        = "ColorTex";	
	public static final String s_ColorTex0       = "ColorTex0";	
	public static final String s_ColorTex1       = "ColorTex1";	
	public static final String s_TempTex         = "TempTex";
	public static final String s_BackgroundColor = "BackgroundColor";
	
	public GLUniformData g_FrontBlenderTexUnit;
	public GLUniformData g_BackBlenderTexUnit;
	public GLUniformData g_DepthBlenderTexUnit;
	public GLUniformData g_ColorTexUnit;
	public GLUniformData g_ColorTex0Unit;
	public GLUniformData g_ColorTex1Unit;
	public GLUniformData g_TempTexUnit;
	public GLUniformData g_AlphaUni;
	public GLUniformData g_backgroundColorUni;

	public ShaderState g_shaderState;
	public ShaderProgram g_shaderDualInit;
	public ShaderProgram g_shaderDualPeel;
	public ShaderProgram g_shaderDualBlend;
	public ShaderProgram g_shaderDualFinal;

	public ShaderProgram g_shaderFrontInit;
	public ShaderProgram g_shaderFrontPeel;
	public ShaderProgram g_shaderFrontBlend;
	public ShaderProgram g_shaderFrontFinal;

	public ShaderProgram g_shaderAverageInit;
	public ShaderProgram g_shaderAverageFinal;

	public ShaderProgram g_shaderWeightedSumInit;
	public ShaderProgram g_shaderWeightedSumFinal;
	
	public float g_opacity = 0.6f;
	public char g_mode = DUAL_PEELING_MODE;
	public boolean g_showOsd = true;
	public boolean g_bShowUI = true;
	public int g_numGeoPasses = 0;

	public boolean g_rotating = false;
	public boolean g_panning = false;
	public boolean g_scaling = false;
	public int g_oldX, g_oldY;
	public int g_newX, g_newY;
	public float g_bbScale = 1.0f;
	public float[] g_bbTrans = new float[]{0.0f, 0.0f, 0.0f};
	public float[] g_rot = new float[]{0.0f, 45.0f};
	public float[] g_pos = new float[]{0.0f, 0.0f, 2.0f};

	static final FloatBuffer g_white = Buffers.newDirectFloatBuffer(new float[]{1.0f,1.0f,1.0f});
	static final FloatBuffer g_black = Buffers.newDirectFloatBuffer(new float[]{0.0f,0.0f,0.0f});
	FloatBuffer g_backgroundColor = g_white;

	public int[]  g_dualBackBlenderFboId = new int[1];
	public int[]  g_dualPeelingSingleFboId = new int[1];
	public int[]  g_dualDepthTexId = new int[2];
	public int[]  g_dualFrontBlenderTexId = new int[2];
	public int[]  g_dualBackTempTexId = new int[2];
	public int[]  g_dualBackBlenderTexId = new int[1];

	public int[]  g_frontFboId = new int[2];
	public int[]  g_frontDepthTexId = new int[2];
	public int[]  g_frontColorTexId = new int[2];
	public int[]  g_frontColorBlenderTexId = new int[1];
	public int[]  g_frontColorBlenderFboId = new int[1];

	public int[] g_accumulationTexId = new int[2];
	public int[] g_accumulationFboId = new int[1];

	int g_drawBuffers[] = {GL2.GL_COLOR_ATTACHMENT0,
			GL2.GL_COLOR_ATTACHMENT1,
			GL2.GL_COLOR_ATTACHMENT2,
			GL2.GL_COLOR_ATTACHMENT3,
			GL2.GL_COLOR_ATTACHMENT4,
			GL2.GL_COLOR_ATTACHMENT5,
			GL2.GL_COLOR_ATTACHMENT6
	};
	
	boolean reloadShaders = false;

	public DualDepthPeeling()
	{
		InitGL();	
	}


	public void InitDualPeelingRenderTargets(GL2 gl)
	{
		gl.glGenTextures(2, g_dualDepthTexId, 0);
		gl.glGenTextures(2, g_dualFrontBlenderTexId, 0);
		gl.glGenTextures(2, g_dualBackTempTexId, 0);
		gl.glGenFramebuffers(1, g_dualPeelingSingleFboId, 0);
		for (int i = 0; i < 2; i++)
		{
			gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualDepthTexId[i]);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S,  GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T,  GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
			
			//gl.glEnable( GL2.GL_PIXEL_UNPACK_BUFFER );
			
			gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0,  GL2.GL_FLOAT_RG32_NV, g_imageWidth, g_imageHeight,
					0,  GL2.GL_RGB,  GL2.GL_FLOAT, null);

			gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualFrontBlenderTexId[i]);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
			gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0,  GL2.GL_RGBA, g_imageWidth, g_imageHeight,
					0,  GL2.GL_RGBA,  GL2.GL_FLOAT, null);

			gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualBackTempTexId[i]);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
			gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0,  GL2.GL_RGBA, g_imageWidth, g_imageHeight,
					0,  GL2.GL_RGBA,  GL2.GL_FLOAT, null);
		}

		gl.glGenTextures(1, g_dualBackBlenderTexId, 0);
		gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualBackBlenderTexId[0]);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
		gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0, GL2.GL_RGB, g_imageWidth, g_imageHeight,
				0, GL2.GL_RGB, GL2.GL_FLOAT, null);

		gl.glGenFramebuffers(1, g_dualBackBlenderFboId, 0);
		gl.glBindFramebuffer( GL2.GL_FRAMEBUFFER, g_dualBackBlenderFboId[0]);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER, GL2.GL_COLOR_ATTACHMENT0,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualBackBlenderTexId[0], 0);

		gl.glBindFramebuffer( GL2.GL_FRAMEBUFFER, g_dualPeelingSingleFboId[0]);

		int j = 0;
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT0,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualDepthTexId[j], 0);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT1,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualFrontBlenderTexId[j], 0);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT2,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualBackTempTexId[j], 0);

		j = 1;
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT3,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualDepthTexId[j], 0);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT4,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualFrontBlenderTexId[j], 0);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT5,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualBackTempTexId[j], 0);

		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT6,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_dualBackBlenderTexId[0], 0);
	}

	//--------------------------------------------------------------------------
	void DeleteDualPeelingRenderTargets(GL2 gl)
	{
		gl.glDeleteFramebuffers(1, g_dualBackBlenderFboId, 0);
		gl.glDeleteFramebuffers(1, g_dualPeelingSingleFboId, 0);
		gl.glDeleteTextures(2, g_dualDepthTexId, 0);
		gl.glDeleteTextures(2, g_dualFrontBlenderTexId, 0);
		gl.glDeleteTextures(2, g_dualBackTempTexId, 0);
		gl.glDeleteTextures(1, g_dualBackBlenderTexId, 0);
	}

	//--------------------------------------------------------------------------
	void InitFrontPeelingRenderTargets(GL2 gl)
	{
		gl.glGenTextures(2, g_frontDepthTexId, 0);
		gl.glGenTextures(2, g_frontColorTexId, 0);
		gl.glGenFramebuffers(2, g_frontFboId, 0);

		for (int i = 0; i < 2; i++)
		{
			gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_frontDepthTexId[i]);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S,  GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T,  GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
			gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0,  GL2.GL_DEPTH_COMPONENT32F,
					g_imageWidth, g_imageHeight, 0,  GL2.GL_DEPTH_COMPONENT,  GL2.GL_FLOAT, null);

			gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_frontColorTexId[i]);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S,  GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T,  GL2.GL_CLAMP);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
			gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
			gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0,  GL2.GL_RGBA, g_imageWidth, g_imageHeight,
					0,  GL2.GL_RGBA,  GL2.GL_FLOAT, null);

			gl.glBindFramebuffer( GL2.GL_FRAMEBUFFER, g_frontFboId[i]);
			gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_DEPTH_ATTACHMENT,
					GL2.GL_TEXTURE_RECTANGLE_ARB, g_frontDepthTexId[i], 0);
			gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT0,
					GL2.GL_TEXTURE_RECTANGLE_ARB, g_frontColorTexId[i], 0);
		}

		gl.glGenTextures(1, g_frontColorBlenderTexId, 0);
		gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_frontColorBlenderTexId[0]);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
		gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0, GL2. GL_RGBA, g_imageWidth, g_imageHeight,
				0,  GL2.GL_RGBA,  GL2.GL_FLOAT, null);

		gl.glGenFramebuffers(1, g_frontColorBlenderFboId, 0);
		gl.glBindFramebuffer( GL2.GL_FRAMEBUFFER, g_frontColorBlenderFboId[0]);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_DEPTH_ATTACHMENT,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_frontDepthTexId[0], 0);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT0,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_frontColorBlenderTexId[0], 0);
	}

	//--------------------------------------------------------------------------
	void DeleteFrontPeelingRenderTargets(GL2 gl)
	{
		gl.glDeleteFramebuffers(2, g_frontFboId, 0);
		gl.glDeleteFramebuffers(1, g_frontColorBlenderFboId, 0);
		gl.glDeleteTextures(2, g_frontDepthTexId, 0);
		gl.glDeleteTextures(2, g_frontColorTexId, 0);
		gl.glDeleteTextures(1, g_frontColorBlenderTexId, 0);
	}

	//--------------------------------------------------------------------------
	void InitAccumulationRenderTargets(GL2 gl)
	{
		gl.glGenTextures(2, g_accumulationTexId, 0);

		gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[0]);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S,  GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T,  GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
		gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0,  GL2.GL_RGBA16F,
				g_imageWidth, g_imageHeight, 0,  GL2.GL_RGBA,  GL2.GL_FLOAT, null);

		gl.glBindTexture( GL2.GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[1]);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_S,  GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_WRAP_T,  GL2.GL_CLAMP);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MIN_FILTER,  GL2.GL_NEAREST);
		gl.glTexParameteri( GL2.GL_TEXTURE_RECTANGLE_ARB,  GL2.GL_TEXTURE_MAG_FILTER,  GL2.GL_NEAREST);
		gl.glTexImage2D( GL2.GL_TEXTURE_RECTANGLE_ARB, 0,  GL2.GL_FLOAT_R32_NV,
				g_imageWidth, g_imageHeight, 0,  GL2.GL_RGBA,  GL2.GL_FLOAT, null);

		gl.glGenFramebuffers(1, g_accumulationFboId, 0);
		gl.glBindFramebuffer( GL2.GL_FRAMEBUFFER, g_accumulationFboId[0]);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT0,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[0], 0);
		gl.glFramebufferTexture2D( GL2.GL_FRAMEBUFFER,  GL2.GL_COLOR_ATTACHMENT1,
				GL2.GL_TEXTURE_RECTANGLE_ARB, g_accumulationTexId[1], 0);

	}

	//--------------------------------------------------------------------------
	void DeleteAccumulationRenderTargets(GL2 gl)
	{
		gl.glDeleteFramebuffers(1, g_accumulationFboId, 0);
		gl.glDeleteTextures(2, g_accumulationTexId, 0);
	}

	//--------------------------------------------------------------------------
	void MakeFullScreenQuad(GL2 gl)
	{
		GLU glu = GLU.createGLU(gl);

		g_quadDisplayList = gl.glGenLists(1);
		gl.glNewList(g_quadDisplayList, GL2.GL_COMPILE);

		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glPushMatrix();
		gl.glLoadIdentity();
		glu.gluOrtho2D(0.0f, 1.0f, 0.0f, 1.0f);
		gl.glBegin(GL2.GL_QUADS);
		{
			gl.glVertex2f(0.0f, 0.0f); 
			gl.glVertex2f(1.0f, 0.0f);
			gl.glVertex2f(1.0f, 1.0f);
			gl.glVertex2f(0.0f, 1.0f);
		}
		gl.glEnd();
		gl.glPopMatrix();

		gl.glEndList();
	}

	//--------------------------------------------------------------------------
	void LoadModel( GL2 gl, String model_filename)
	{
		g_model = new Model();
		System.err.println("loading OBJ...\n");

		g_model.loadModelFromFile(DemosDataAnchor.class, model_filename );

		System.err.println("compiling mesh...\n");
		g_model.compileModel();

		System.err.println(g_model.getPositionCount() + " vertices");
		System.err.println((g_model.getIndexCount()/3) + " triangles");
		int totalVertexSize = g_model.getCompiledVertexCount() * Buffers.SIZEOF_FLOAT;
		int totalIndexSize = g_model.getCompiledIndexCount() * Buffers.SIZEOF_INT;

		gl.glGenBuffers(1, g_vboId, 0);
		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, g_vboId[0]);
		gl.glBufferData(GL2.GL_ARRAY_BUFFER, totalVertexSize, g_model.getCompiledVertices(), GL2.GL_STATIC_DRAW);
		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);

		gl.glGenBuffers(1, g_eboId, 0);
		gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, g_eboId[0]);
		gl.glBufferData(GL2.GL_ELEMENT_ARRAY_BUFFER, totalIndexSize, g_model.getCompiledIndices(), GL2.GL_STATIC_DRAW);
		gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);

		float[] modelMin = new float[3];
		float[] modelMax = new float[3];
		g_model.computeBoundingBox(modelMin, modelMax);

		float[] diag = new float[]{ modelMax[0] - modelMin[0],
				modelMax[1] - modelMin[1],
				modelMax[2] - modelMin[2] };
		g_bbScale = (float)(1.0 / Math.sqrt(diag[0]*diag[0] + diag[1]*diag[1] + diag[2]*diag[2]) * 1.5);
		g_bbTrans = new float[]{ (float)( -g_bbScale * (modelMin[0] + 0.5 * diag[0])), 
				(float)( -g_bbScale * (modelMin[1] + 0.5 * diag[1]) ), 
				(float)( -g_bbScale * (modelMin[2] + 0.5 * diag[2]) ) };
	}

	//--------------------------------------------------------------------------
	void DrawModel(GL2 gl)
	{
		gl.glBindBuffer( GL2.GL_ARRAY_BUFFER, g_vboId[0]);
		gl.glBindBuffer( GL2.GL_ELEMENT_ARRAY_BUFFER, g_eboId[0]);
		int stride = g_model.getCompiledVertexSize() * Buffers.SIZEOF_FLOAT;
		int normalOffset = g_model.getCompiledNormalOffset() * Buffers.SIZEOF_FLOAT;
		gl.glVertexPointer(g_model.getPositionSize(), GL2.GL_FLOAT, stride, 0);
		gl.glNormalPointer(GL2.GL_FLOAT, stride, normalOffset);
		gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL2.GL_NORMAL_ARRAY);

		gl.glDrawElements( GL2.GL_TRIANGLES, g_model.getCompiledIndexCount(), GL2.GL_UNSIGNED_INT, 0);

		gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
		gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
		gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL2.GL_NORMAL_ARRAY);

		g_numGeoPasses++;
	}
	
    public GLCanvas GetCanvas()
    {
        return m_kCanvas;
    }

    ShaderProgram build(GL2ES2 gl, String basename, boolean link) {
    	ShaderProgram sp = new ShaderProgram();
    	ShaderCode vp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, DualDepthPeeling.class,
                "shader", null, basename, false);
    	ShaderCode fp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, DualDepthPeeling.class,
				  "shader", null, basename, false);
    	sp.add(vp);
    	sp.add(fp);
    	if(link && !sp.link(gl, System.err)) {
    		throw new GLException("Couldn't link program: "+sp);
    	}    	
    	return sp;
    }
    ShaderProgram build(GL2ES2 gl, String[] basenames, boolean link) {
    	ShaderProgram sp = new ShaderProgram();
		ShaderCode vp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, basenames.length, DualDepthPeeling.class,
				"shader", basenames, null, null, false);
    	sp.add(vp);
    	ShaderCode fp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, basenames.length, DualDepthPeeling.class,
				  "shader", basenames, null, null, false);
    	sp.add(fp);
    	if(link && !sp.link(gl, System.err)) {
    		throw new GLException("Couldn't link program: "+sp);
    	}    	
    	return sp;
    }
    
	//--------------------------------------------------------------------------
	void BuildShaders(GL2 gl)
	{
		System.err.println("\nloading shaders...\n");

		g_shaderState = new ShaderState();
		// g_shaderState.setVerbose(true);
		
		g_shaderDualInit = build(gl, "dual_peeling_init", true);
		g_shaderDualPeel = build(gl, new String[] { "shade", "dual_peeling_peel" }, true);
		g_shaderDualBlend = build(gl, "dual_peeling_blend", true);
		g_shaderDualFinal = build(gl, "dual_peeling_final", true);
				
		g_shaderFrontInit = build(gl, new String[] { "shade", "front_peeling_init" }, true);
		g_shaderFrontPeel = build(gl, new String[] { "shade", "front_peeling_peel" }, true);
		g_shaderFrontBlend = build(gl, "front_peeling_blend", true);
		g_shaderFrontFinal = build(gl, "front_peeling_final", true);
		
		g_shaderAverageInit = build(gl, new String[] { "shade", "wavg_init" }, true);
		g_shaderAverageFinal = build(gl, "wavg_final", true);
		
		g_shaderWeightedSumInit = build(gl, new String[] { "shade", "wsum_init" }, true);		
		g_shaderWeightedSumFinal = build(gl, "wsum_final", true);
		
		g_DepthBlenderTexUnit = new GLUniformData(s_DepthBlenderTex, 0);
		g_shaderState.ownUniform(g_DepthBlenderTexUnit);
		
		g_FrontBlenderTexUnit = new GLUniformData(s_FrontBlenderTex, 1);
		g_shaderState.ownUniform(g_FrontBlenderTexUnit);
		
		g_BackBlenderTexUnit = new GLUniformData(s_BackBlenderTex, 2);
		g_shaderState.ownUniform(g_BackBlenderTexUnit);
		
		g_TempTexUnit = new GLUniformData(s_TempTex, 0);
		g_shaderState.ownUniform(g_TempTexUnit); 
		
		g_AlphaUni = new GLUniformData("Alpha", g_opacity);
		g_shaderState.ownUniform(g_AlphaUni);

		g_backgroundColorUni = new GLUniformData(s_BackgroundColor, 3, g_backgroundColor);
		g_shaderState.ownUniform(g_backgroundColorUni);
		
		g_ColorTexUnit = new GLUniformData(s_ColorTex, 0);
		g_shaderState.ownUniform(g_ColorTexUnit);
		
		g_ColorTex0Unit = new GLUniformData(s_ColorTex0, 0);
		g_shaderState.ownUniform(g_ColorTex0Unit);
		
		g_ColorTex1Unit = new GLUniformData(s_ColorTex1, 1);
		g_shaderState.ownUniform(g_ColorTex1Unit);		
	}

	//--------------------------------------------------------------------------
	void DestroyShaders(GL2 gl)
	{
		g_shaderState.release(gl, false, false, false);		
		g_shaderDualInit.destroy(gl);
		g_shaderDualPeel.destroy(gl);
		g_shaderDualBlend.destroy(gl);
		g_shaderDualFinal.destroy(gl);

		g_shaderFrontInit.destroy(gl);
		g_shaderFrontPeel.destroy(gl);
		g_shaderFrontBlend.destroy(gl);
		g_shaderFrontFinal.destroy(gl);

		g_shaderAverageInit.destroy(gl);
		g_shaderAverageFinal.destroy(gl);

		g_shaderWeightedSumInit.destroy(gl);
		g_shaderWeightedSumFinal.destroy(gl);
	}

	//--------------------------------------------------------------------------
	void ReloadShaders(GL2 gl)
	{
		DestroyShaders(gl);
		BuildShaders(gl);
	}

	/** GLCanvas for Java/JOGL */
	private GLCanvas m_kCanvas;

	void InitGL()
	{ 

	  	GLProfile kProfile = GLProfile.getMaxProgrammable(true);
		GLCapabilities kGlCapabilities = new GLCapabilities(kProfile);
		kGlCapabilities.setHardwareAccelerated(true);
		m_kCanvas = new GLCanvas(kGlCapabilities);
		m_kCanvas.setSize(g_imageWidth, g_imageHeight);
		m_kCanvas.addGLEventListener( this );       
		m_kCanvas.addKeyListener( this );       
		m_kCanvas.addMouseListener( this );       
		m_kCanvas.addMouseMotionListener( this );       
	}

	//--------------------------------------------------------------------------
	void RenderDualPeeling(GL2 gl)
	{
		gl.glDisable(GL2.GL_DEPTH_TEST);
		gl.glEnable(GL2.GL_BLEND);

		// ---------------------------------------------------------------------
		// 1. Initialize Min-Max Depth Buffer
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, g_dualPeelingSingleFboId[0]);

		// Render targets 1 and 2 store the front and back colors
		// Clear to 0.0 and use MAX blending to filter written color
		// At most one front color and one back color can be written every pass
		gl.glDrawBuffers(2, g_drawBuffers, 1);
		gl.glClearColor(0, 0, 0, 0);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT);

		// Render target 0 stores (-minDepth, maxDepth, alphaMultiplier)
		gl.glDrawBuffer(g_drawBuffers[0]);
		gl.glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT);
		gl.glBlendEquation(GL2.GL_MAX);

		g_shaderState.attachShaderProgram(gl, g_shaderDualInit, true);
		DrawModel(gl);
		g_shaderState.useProgram(gl, false);

		// ---------------------------------------------------------------------
		// 2. Dual Depth Peeling + Blending
		// ---------------------------------------------------------------------

		// Since we cannot blend the back colors in the geometry passes,
		// we use another render target to do the alpha blending
		//glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_dualBackBlenderFboId);
		gl.glDrawBuffer(g_drawBuffers[6]);
		gl.glClearColor(g_backgroundColor.get(0), g_backgroundColor.get(1), g_backgroundColor.get(2), 0);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT);

		int currId = 0;

		for (int pass = 1; g_useOQ || pass < g_numPasses; pass++) {
			currId = pass % 2;
			int prevId = 1 - currId;
			int bufId = currId * 3;

			//glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_dualPeelingFboId[currId]);

			gl.glDrawBuffers(2, g_drawBuffers, bufId+1);
			gl.glClearColor(0, 0, 0, 0);
			gl.glClear(GL2.GL_COLOR_BUFFER_BIT);

			gl.glDrawBuffer(g_drawBuffers[bufId+0]);
			gl.glClearColor(-MAX_DEPTH, -MAX_DEPTH, 0, 0);
			gl.glClear(GL2.GL_COLOR_BUFFER_BIT);

			// Render target 0: RG32F MAX blending
			// Render target 1: RGBA MAX blending
			// Render target 2: RGBA MAX blending
			gl.glDrawBuffers(3, g_drawBuffers, bufId+0);
			gl.glBlendEquation(GL2.GL_MAX);

			// uses g_DepthBlenderTexUnit
			// uses g_FrontBlenderTexUnit
			// uses g_AlphaUni			
			g_shaderState.attachShaderProgram(gl, g_shaderDualPeel, true);
			GLHelper.bindTextureRECT(gl, g_dualDepthTexId[prevId], g_DepthBlenderTexUnit.intValue());
			GLHelper.bindTextureRECT(gl, g_dualFrontBlenderTexId[prevId], g_FrontBlenderTexUnit.intValue());
			DrawModel(gl);
			g_shaderState.useProgram(gl, false);

			// Full screen pass to alpha-blend the back color
			gl.glDrawBuffer(g_drawBuffers[6]);

			gl.glBlendEquation(GL2.GL_FUNC_ADD);
			gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);

			if (g_useOQ) {
				gl.glBeginQuery(GL2.GL_SAMPLES_PASSED, g_queryId[0]);
			}

			g_TempTexUnit.setData(0);
			g_shaderState.attachShaderProgram(gl, g_shaderDualBlend, true);
			GLHelper.bindTextureRECT(gl, g_dualBackTempTexId[currId], g_TempTexUnit.intValue());
			gl.glCallList(g_quadDisplayList);
			g_shaderState.useProgram(gl, false);

			if (g_useOQ) {
				gl.glEndQuery(GL2.GL_SAMPLES_PASSED);
				int[] sample_count = new int[]{0};
				gl.glGetQueryObjectuiv(g_queryId[0], GL2.GL_QUERY_RESULT, sample_count, 0);
				if (sample_count[0] == 0) {
					break;
				}
			}
		}

		gl.glDisable(GL2.GL_BLEND);

		// ---------------------------------------------------------------------
		// 3. Final Pass
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);
		gl.glDrawBuffer(GL2.GL_BACK);

		// use g_FrontBlenderTexUnit
		// use g_BackBlenderTexUnit		
		g_shaderState.attachShaderProgram(gl, g_shaderDualFinal, true);
		GLHelper.bindTextureRECT(gl, g_dualFrontBlenderTexId[currId], g_FrontBlenderTexUnit.intValue());
		GLHelper.bindTextureRECT(gl, g_dualBackBlenderTexId[0], g_BackBlenderTexUnit.intValue());
		gl.glCallList(g_quadDisplayList);
		g_shaderState.useProgram(gl, false);
	}

	//--------------------------------------------------------------------------
	void RenderFrontToBackPeeling(GL2 gl)
	{
		// ---------------------------------------------------------------------
		// 1. Initialize Min Depth Buffer
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, g_frontColorBlenderFboId[0]);
		gl.glDrawBuffer(g_drawBuffers[0]);

		gl.glClearColor(0, 0, 0, 1);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);

		gl.glEnable(GL2.GL_DEPTH_TEST);

		// uses g_AlphaUni			
		g_shaderState.attachShaderProgram(gl, g_shaderFrontInit, true);
		DrawModel(gl);
		g_shaderState.useProgram(gl, false);

		// ---------------------------------------------------------------------
		// 2. Depth Peeling + Blending
		// ---------------------------------------------------------------------

		int numLayers = (g_numPasses - 1) * 2;
		for (int layer = 1; g_useOQ || layer < numLayers; layer++) {
			int currId = layer % 2;
			int prevId = 1 - currId;

			gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, g_frontFboId[currId]);
			gl.glDrawBuffer(g_drawBuffers[0]);

			gl.glClearColor(0, 0, 0, 0);
			gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);

			gl.glDisable(GL2.GL_BLEND);
			gl.glEnable(GL2.GL_DEPTH_TEST);

			if (g_useOQ) {
				gl.glBeginQuery(GL2.GL_SAMPLES_PASSED, g_queryId[0]);
			}

			// uses g_DepthBlenderTexUnit
			// uses g_FrontBlenderTexUnit
			// uses g_AlphaUni			
			g_shaderState.attachShaderProgram(gl, g_shaderDualPeel, true);			
			GLHelper.bindTextureRECT(gl, g_frontDepthTexId[prevId], g_DepthBlenderTexUnit.intValue());
			DrawModel(gl);
			g_shaderState.useProgram(gl, false);

			if (g_useOQ) {
				gl.glEndQuery(GL2.GL_SAMPLES_PASSED);
			}


			gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, g_frontColorBlenderFboId[0]);
			gl.glDrawBuffer(g_drawBuffers[0]);

			gl.glDisable(GL2.GL_DEPTH_TEST);
			gl.glEnable(GL2.GL_BLEND);

			gl.glBlendEquation(GL2.GL_FUNC_ADD);
			gl.glBlendFuncSeparate(GL2.GL_DST_ALPHA, GL2.GL_ONE,
					GL2.GL_ZERO, GL2.GL_ONE_MINUS_SRC_ALPHA);

			// uses g_TempTexUnit
			g_shaderState.attachShaderProgram(gl, g_shaderDualBlend, true);
			GLHelper.bindTextureRECT(gl, g_frontColorTexId[currId], g_TempTexUnit.intValue());
			gl.glCallList(g_quadDisplayList);
			g_shaderState.useProgram(gl, false);

			gl.glDisable(GL2.GL_BLEND);

			if (g_useOQ) {
				int[] sample_count = new int[]{0};
				gl.glGetQueryObjectuiv(g_queryId[0], GL2.GL_QUERY_RESULT, sample_count, 0);
				if (sample_count[0] == 0) {
					break;
				}
			}
		}

		// ---------------------------------------------------------------------
		// 3. Final Pass
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);
		gl.glDrawBuffer(GL2.GL_BACK);
		gl.glDisable(GL2.GL_DEPTH_TEST);

		// uses g_backgroundColorUni
		g_shaderState.attachShaderProgram(gl, g_shaderFrontFinal, true);
		GLHelper.bindTextureRECT(gl, g_frontColorBlenderTexId[0], g_ColorTexUnit.intValue());
		gl.glCallList(g_quadDisplayList);
		g_shaderState.useProgram(gl, false);
	}

	//--------------------------------------------------------------------------
	void RenderAverageColors(GL2 gl)
	{
		gl.glDisable(GL2.GL_DEPTH_TEST);

		// ---------------------------------------------------------------------
		// 1. Accumulate Colors and Depth Complexity
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, g_accumulationFboId[0]);
		gl.glDrawBuffers(2, g_drawBuffers, 0);

		gl.glClearColor(0, 0, 0, 0);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT);

		gl.glBlendEquation(GL2.GL_FUNC_ADD);
		gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE);
		gl.glEnable(GL2.GL_BLEND);

		// uses g_AlphaUni			
		g_shaderState.attachShaderProgram(gl, g_shaderAverageInit, true);
		DrawModel(gl);
		g_shaderState.useProgram(gl, false);

		gl.glDisable(GL2.GL_BLEND);


		// ---------------------------------------------------------------------
		// 2. Approximate Blending
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);
		gl.glDrawBuffer(GL2.GL_BACK);

		// uses g_backgroundColorUni
		g_shaderState.attachShaderProgram(gl, g_shaderAverageFinal, true);
		GLHelper.bindTextureRECT(gl, g_accumulationTexId[0], g_ColorTex0Unit.intValue());
		GLHelper.bindTextureRECT(gl, g_accumulationTexId[1], g_ColorTex1Unit.intValue());
		gl.glCallList(g_quadDisplayList);
		g_shaderState.useProgram(gl, false);
	}

	//--------------------------------------------------------------------------
	void RenderWeightedSum(GL2 gl)
	{
		gl.glDisable(GL2.GL_DEPTH_TEST);

		// ---------------------------------------------------------------------
		// 1. Accumulate (alpha * color) and (alpha)
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, g_accumulationFboId[0]);
		gl.glDrawBuffer(g_drawBuffers[0]);

		gl.glClearColor(0, 0, 0, 0);
		gl.glClear(GL2.GL_COLOR_BUFFER_BIT);

		gl.glBlendEquation(GL2.GL_FUNC_ADD);
		gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE);
		gl.glEnable(GL2.GL_BLEND);

		g_shaderState.attachShaderProgram(gl, g_shaderWeightedSumInit, true);
		DrawModel(gl);
		g_shaderState.useProgram(gl, false);

		gl.glDisable(GL2.GL_BLEND);

		// ---------------------------------------------------------------------
		// 2. Weighted Sum
		// ---------------------------------------------------------------------

		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);
		gl.glDrawBuffer(GL2.GL_BACK);

		g_shaderState.attachShaderProgram(gl, g_shaderWeightedSumFinal, true);
		GLHelper.bindTextureRECT(gl, g_accumulationTexId[0], this.g_ColorTexUnit.intValue());
		gl.glCallList(g_quadDisplayList);
		g_shaderState.useProgram(gl, false);
	}


	public static void main(String[] args) {
		System.out.println("dual_depth_peeling - sample comparing multiple order independent transparency techniques\n");
		System.out.println("  Commands:\n");
		System.out.println("     A/D       - Change uniform opacity\n");
		System.out.println("     1         - Dual peeling mode\n");
		System.out.println("     2         - Front to back peeling mode\n");
		System.out.println("     3         - Weighted average mode\n");
		System.out.println("     4         - Weighted sum mode\n");
		System.out.println("     R         - Reload all shaders\n");
		System.out.println("     B         - Change background color\n");
		System.out.println("     Q         - Toggle occlusion queries\n");
		System.out.println("     +/-       - Change number of geometry passes\n\n");


		DualDepthPeeling kWorld = new DualDepthPeeling();
		Frame frame = new Frame("Dual Depth Peeling");
		frame.add( kWorld.GetCanvas() );
		frame.setSize(kWorld.GetCanvas().getWidth(), kWorld.GetCanvas().getHeight());
		/* Animator serves the purpose of the idle function, calls display: */
		final Animator animator = new Animator( kWorld.GetCanvas() );
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				// Run this on another thread than the AWT event queue to
				// avoid deadlocks on shutdown on some platforms
				new Thread(new Runnable() {
					public void run() {
						animator.stop();
						System.exit(0);
					}
				}).start();
			}
		});
		frame.setVisible(true);
		animator.start();
	}


	@Override
	public void keyTyped(KeyEvent e) {
		// TODO Auto-generated method stub

	}


	@Override
	public void keyPressed(KeyEvent e) {
		// TODO Auto-generated method stub

	}


	@Override
	public void keyReleased(KeyEvent e) {
		switch(e.getKeyChar())
		{
		case 8:
			g_bShowUI = !g_bShowUI;
			break;
		case 'q':
			g_useOQ = !g_useOQ;
			break;
		case '+':
			g_numPasses++;
			break;
		case '-':
			g_numPasses--;
			break;
		case 'b':
			g_backgroundColor = (g_backgroundColor == g_white) ? g_black : g_white;
			g_backgroundColorUni.setData(g_backgroundColor);
			break;
		case 'o':
			g_showOsd = !g_showOsd;
			break;
		case 'r':
			reloadShaders = true;
			break;
		case '1':
			g_mode = DUAL_PEELING_MODE;
			break;
		case '2':
			g_mode = F2B_PEELING_MODE;
			break;
		case '3':
			g_mode = WEIGHTED_AVERAGE_MODE;
			break;
		case '4':
			g_mode = WEIGHTED_SUM_MODE;
			break;
		case 'a':
			g_opacity -= 0.05;
			g_opacity = (float)Math.max(g_opacity, 0.0);
			g_AlphaUni.setData(g_opacity);
			break;
		case 'd':
			g_opacity += 0.05;
			g_opacity = (float)Math.min(g_opacity, 1.0);
			g_AlphaUni.setData(g_opacity);
			break;
		}
	}


	@Override
	public void display(GLAutoDrawable arg0) {
		GL2 gl = arg0.getGL().getGL2();
		
		if(reloadShaders) {
			reloadShaders = false;
			ReloadShaders(gl);
		}
		GLU glu = GLU.createGLU(gl);
		
		g_numGeoPasses = 0;

		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glLoadIdentity();
		glu.gluLookAt(g_pos[0], g_pos[1], g_pos[2], g_pos[0], g_pos[1], 0, 0, 1, 0);
		gl.glRotatef(g_rot[0], 1, 0, 0);
		gl.glRotatef(g_rot[1], 0, 1, 0);
		gl.glTranslatef(g_bbTrans[0], g_bbTrans[1], g_bbTrans[2]);
		gl.glScalef(g_bbScale, g_bbScale, g_bbScale);

		switch (g_mode) {
		case DUAL_PEELING_MODE:
			RenderDualPeeling(gl);
			break;
		case F2B_PEELING_MODE:
			RenderFrontToBackPeeling(gl);
			break;
		case WEIGHTED_AVERAGE_MODE:
			RenderAverageColors(gl);
			break;
		case WEIGHTED_SUM_MODE:
			RenderWeightedSum(gl);
			break;
		}

        /* Call swapBuffers to render on-screen: */
		arg0.swapBuffers();
	}


	@Override
	public void dispose(GLAutoDrawable arg0) {
		// TODO Auto-generated method stub

	}


	@Override
	public void init(GLAutoDrawable drawable) {
		System.err.println( "init" );
		GL2 gl = drawable.getGL().getGL2();
		
		m_kCanvas.setAutoSwapBufferMode( false );

		// Allocate render targets first
		try {
			InitDualPeelingRenderTargets(gl);
		} catch ( GLException e )
		{
			try {
				InitDualPeelingRenderTargets(gl);
			} catch ( GLException e1 )
			{
				System.err.println( e1.getStackTrace() );
			}
		}
		InitFrontPeelingRenderTargets(gl);
		InitAccumulationRenderTargets(gl);
		gl.glBindFramebuffer(GL2.GL_FRAMEBUFFER, 0);

		BuildShaders(gl);
		LoadModel(gl, MODEL_FILENAME);
		MakeFullScreenQuad(gl);

		gl.glDisable(GL2.GL_CULL_FACE);
		gl.glDisable(GL2.GL_LIGHTING);
		gl.glDisable(GL2.GL_NORMALIZE);

		gl.glGenQueries(1, g_queryId, 0);
	}


	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        GL2 gl = drawable.getGL().getGL2();

		if (g_imageWidth != width || g_imageHeight != height)
		{
			g_imageWidth = width;
			g_imageHeight = height;

			DeleteDualPeelingRenderTargets(gl);
			InitDualPeelingRenderTargets(gl);

			DeleteFrontPeelingRenderTargets(gl);
			InitFrontPeelingRenderTargets(gl);

			DeleteAccumulationRenderTargets(gl);
			InitAccumulationRenderTargets(gl);
		}

		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glLoadIdentity();

		GLU glu = GLU.createGLU(gl);
		glu.gluPerspective(FOVY, (float)g_imageWidth/(float)g_imageHeight, ZNEAR, ZFAR);
		gl.glMatrixMode(GL2.GL_MODELVIEW);

		gl.glViewport(0, 0, g_imageWidth, g_imageHeight);
	}


	@Override
	public void mouseDragged(MouseEvent e) {

		g_oldX = g_newX; g_oldY = g_newY;
		g_newX = e.getX();
		g_newY = e.getY();

		float rel_x = (g_newX - g_oldX) / (float)g_imageWidth;
		float rel_y = (g_newY - g_oldY) / (float)g_imageHeight;
		if (g_rotating)
		{
			g_rot[1] += (rel_x * 180);
			g_rot[0] += (rel_y * 180);
		}
		else if (g_panning)
		{
			g_pos[0] -= rel_x;
			g_pos[1] += rel_y;
		}
		else if (g_scaling)
		{
			g_pos[2] -= rel_y * g_pos[2];
		}
	
	}


	@Override
	public void mouseMoved(MouseEvent e) {}


	@Override
	public void mouseClicked(MouseEvent e) {}


	@Override
	public void mousePressed(MouseEvent e) {

		g_newX = e.getX();
		g_newY = e.getY();

		g_scaling = false;
		g_panning = false;
		g_rotating = false;
		
		if (e.getButton() == MouseEvent.BUTTON1)
		{
			if (e.isShiftDown()) {
				g_scaling = true;
			} else if (e.isControlDown()) {
				g_panning = true;
			} else {
				g_rotating = true;
			}
		}
	}


	@Override
	public void mouseReleased(MouseEvent e) {}


	@Override
	public void mouseEntered(MouseEvent e) {}


	@Override
	public void mouseExited(MouseEvent e) {}
}