content: package edu.stanford.rsl.conrad.opencl.rendering;
  
  /*
   * JCuda - Java bindings for NVIDIA CUDA driver and runtime API
   * http://www.jcuda.org
   *
   * DISCLAIMER: THIS SOFTWARE IS PROVIDED WITHOUT WARRANTY OF ANY KIND
   * If you find any bugs or errors, contact me at http://www.jcuda.org
   *
   * LICENSE: THIS SOFTWARE IS FREE FOR NON-COMMERCIAL USE ONLY
   * For non-commercial applications, you may use this software without
   * any restrictions. If you wish to use it for commercial purposes,
   * contact me at http://www.jcuda.org
   */
  
  import java.awt.*;
  import java.awt.event.*;
  import java.io.*;
  import java.nio.ByteBuffer;
  import java.nio.FloatBuffer;
  import java.nio.IntBuffer;
  
  import javax.media.opengl.*;
  import javax.media.opengl.awt.GLJPanel;
  import javax.swing.*;
  import javax.swing.event.*;
  
  import com.jogamp.opencl.CLBuffer;
  import com.jogamp.opencl.CLCommandQueue;
  import com.jogamp.opencl.CLDevice;
  import com.jogamp.opencl.CLImage2d;
  import com.jogamp.opencl.CLImage3d;
  import com.jogamp.opencl.CLImageFormat;
  import com.jogamp.opencl.CLKernel;
  import com.jogamp.opencl.CLPlatform;
  import com.jogamp.opencl.CLProgram;
  import com.jogamp.opencl.CLImageFormat.ChannelOrder;
  import com.jogamp.opencl.CLImageFormat.ChannelType;
  import com.jogamp.opencl.CLMemory.Mem;
  import com.jogamp.opencl.gl.CLGLBuffer;
  import com.jogamp.opencl.gl.CLGLContext;
  import com.jogamp.opengl.util.Animator;
  import com.jogamp.opengl.util.awt.TextRenderer;
  
  import edu.stanford.rsl.conrad.opencl.OpenCLUtil;
  
  /**
   * A sample illustrating how to use textures with JCuda. This program uses
   * the CUBIN file that is created by the "volumeRender" program from the
   * NVIDIA CUDA samples web site. <br />
   * <br />
   * The program loads an 8 bit RAW volume data set and copies it into a 
   * 3D texture. The texture is accessed by the kernel to render an image
   * of the volume data. The resulting image is written into a pixel 
   * buffer object (PBO) which is then displayed using JOGL.
   */
  public class OpenCLTextureRendering extends JFrame implements GLEventListener, MouseControlable
  {
  
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = -6181935230597653012L;
  
  
  
  	/**
  	 * Entry point for this sample.
  	 * 
  	 * @param args not used
  	 */
  	public static void main(String args[])
  	{
  		startSample("Bucky.raw", 32, 32, 32, false);
  
  		// Other input files may be obtained from http://www.volvis.org
  		//startSample("mri_ventricles.raw", 256, 256, 124, false);
  		//startSample("backpack8.raw", 512, 512, 373, false);
  		//startSample("foot.raw", 256, 256, 256, false);
  	}
  
  
  	/**
  	 * Starts this sample with the data that is read from the file
  	 * with the given name. The data is assumed to have the 
  	 * specified dimensions.
  	 * 
  	 * @param fileName The name of the volume data file to load
  	 * @param sizeX The size of the data set in X direction
  	 * @param sizeY The size of the data set in Y direction
  	 * @param sizeZ The size of the data set in Z direction
  	 * @param stereoMode Whether stereo mode should be used
  	 */
  	private static void startSample(
  			String fileName, final int sizeX, final int sizeY, 
  			final int sizeZ, final boolean stereoMode)
  	{
  		// Try to read the specified file
  		byte data[] = null;
  		try
  		{
  			int size = sizeX * sizeY * sizeZ; 
  			FileInputStream fis = new FileInputStream(fileName);
  			data = new byte[size];
  			fis.read(data);
  		}
  		catch (IOException e)
  		{
  			System.err.println("Could not load input file");
  			e.printStackTrace();
  			return;
  		}
  
  		// Start the sample with the data that was read from the file
  		final byte volumeData[] = data;
  		//SwingUtilities.invokeLater(new Runnable()
  		//{
  			//public void run()
  			//{
  				OpenCLTextureRendering sample = new OpenCLTextureRendering(
  						volumeData, sizeX, sizeY, sizeZ, stereoMode);
  				sample.start();
  			//}
  		//});
  	}
  
  	protected float transferFunc[] = new float[]
  	                                           { 
  			0.0f, 0.0f, 0.0f, 0.0f, 
  			1.0f, 0.0f, 0.0f, 1.0f, 
  			1.0f, 0.5f, 0.0f, 1.0f, 
  			1.0f, 1.0f, 0.0f, 1.0f, 
  			0.0f, 1.0f, 0.0f, 1.0f,
  			0.0f, 1.0f, 1.0f, 1.0f, 
  			0.0f, 0.0f, 1.0f, 1.0f, 
  			1.0f, 0.0f, 1.0f, 1.0f, 
  			0.0f, 0.0f, 0.0f, 0.0f
  	                                           };
  
  	/**
  	 * Whether the initialization method of this GLEventListener has already
  	 * been called
  	 */
  	protected boolean initialized = false;
  
  	/**
  	 * Whether the stereo mode is enabled
  	 */
  	private boolean stereoMode = false;
  
  	/**
  	 * The GL component which is used for rendering
  	 */
  	private GLJPanel glComponentL;
  
  	/**
  	 * The GL component which is used for the other eye
  	 */
  	private GLJPanel glComponentR;
  
  	/**
  	 * Text renderer for status messages
  	 */
  	protected TextRenderer renderer;
  
  	/**
  	 * The animator used for rendering
  	 */
  	Animator animatorL;
  
  	/**
  	 * The animator for the other eye
  	 */
  	Animator animatorR;
  
  	/**
  	 * The OpenCL context
  	 */
  	protected CLGLContext glCtx;
  
  	/**
  	 * The OpenCL program
  	 */
  	protected CLProgram program;
  
  	/**
  	 * The OpenCL device
  	 */
  	private CLDevice device;
  	
  	/**
  	 * The OpenCL kernel function binding
  	 */
  	protected CLKernel kernelFunction;
  
  	/**
  	 * The OpenCL command queue
  	 */
  	protected CLCommandQueue commandQueue;	
  
  	/**
  	 * The 3D volume texture reference
  	 */
  	protected CLImage3d<IntBuffer> tex = null;
  	
  	/**
  	 * The 1D transfer texture reference
  	 */
  	protected CLImage2d<FloatBuffer> transferTex = null;
  
  	
  	protected CLBuffer<IntBuffer> hvolumeBuffer = null;
  	
  	protected CLBuffer<FloatBuffer> transferFctBuffer =null;
  	
  	/**
  	 * The volume data that is to be rendered
  	 */
  	protected byte[] h_volume;
  	
  	/**
  	 * The inverted view matrix, which will be copied to the global
  	 * variable of the kernel.
  	 */
  	protected CLBuffer<FloatBuffer> invViewMatrix;
  	
  	/**
  	 * The output of CL
  	 */
  	protected CLGLBuffer<?> d_output;
  	
  	/**
  	 * The width of the rendered area and the PBO
  	 */
  	protected int width = 0;
  
  	/**
  	 * The height of the rendered area and the PBO
  	 */
  	protected int height = 0;
  
  	/**
  	 * The size of the volume data that is to be rendered
  	 */
  	protected int[] volumeSize = null;
  
  	/**
  	 * The block size for the kernel execution
  	 */
  	protected static int blockSize[] = {16, 16};
  	
  	/**
  	 * The block size for the kernel execution
  	 */
  	protected float[] gridSize;
  
  	/**
  	 * The density of the rendered volume data
  	 */
  	protected float density = 0.05f;
  
  	/**
  	 * The brightness of the rendered volume data
  	 */
  	protected float brightness = 1.0f;
  
  	/**
  	 * The transferOffset of the rendered volume data
  	 */
  	protected float transferOffset = 0.0f;
  
  	/**
  	 * The transferScale of the rendered volume data
  	 */
  	protected float transferScale = 1.0f;
  
  	/**
  	 * The OpenGL pixel buffer object
  	 */
  	int pbo = 0;
  
  	/**
  	 * The translation in X-direction
  	 */
  	private float translationX = 0;
  
  	/**
  	 * The translation in Y-direction
  	 */
  	private float translationY = 0;
  
  	/**
  	 * The translation in Z-direction
  	 */
  	private float translationZ = -4;
  
  	/**
  	 * The rotation about the X-axis, in degrees
  	 */
  	private float rotationX = 0;
  
  	protected JFrame frame = null;
  
  	/**
  	 * The rotation about the Y-axis, in degrees
  	 */
  	private float rotationY = 0;
  
  	/**
  	 * The System.nanoTime() of the previous rendered frame.
  	 */
  	private long prevFrameNanoTime = 0;
  
  
  
  	/**
  	 * Creates a new JCudaTextureSample that displays the given volume
  	 * data, which has the specified size.
  	 * 
  	 * @param volumeData The volume data
  	 * @param sizeX The size of the data set in X direction
  	 * @param sizeY The size of the data set in Y direction
  	 * @param sizeZ The size of the data set in Z direction
  	 * @param stereoMode Whether stereo mode should be used
  	 */
  	public OpenCLTextureRendering(
  			byte volumeData[], int sizeX, int sizeY, int sizeZ, boolean stereoMode)
  	{
  		volumeSize = new int[3];
  		h_volume = volumeData;
  		volumeSize[0] = sizeX;
  		volumeSize[1] = sizeY;
  		volumeSize[2] = sizeZ;
  		
  
  		this.stereoMode = stereoMode;
  		if (stereoMode)
  		{
  			width = 280;
  			height = 280;
  		}
  		else
  		{
  			width = 800;
  			height = 800;
  		}
  
  
  
  		// Create the main frame
  		frame = new JFrame("OpenCL 3D texture volume rendering");
  
  		//start();
  
  	}
  
  	public void start(){
  
  		// Initialize the GL component 
  		glComponentL = new GLJPanel();
  		glComponentL.addGLEventListener(this);
  		if (stereoMode)
  		{
  			glComponentR = new GLJPanel();
  			glComponentR.addGLEventListener(this);
  		}
  
  		// Initialize the mouse controls
  		MouseControl mouseControl = new MouseControl(this);
  		glComponentL.addMouseMotionListener(mouseControl);
  		glComponentL.addMouseWheelListener(mouseControl);
  
  
  		frame.addWindowListener(new WindowAdapter()
  		{
  			public void windowClosing(WindowEvent e)
  			{
  				runExit();
  			}
  		});
  		frame.setLayout(new BorderLayout());
  		glComponentL.setPreferredSize(new Dimension(width, height));
  		JPanel p = new JPanel(new GridLayout(1,1));
  		p.add(glComponentL);
  		if (stereoMode)
  		{
  			p.setLayout(new GridLayout(1,2));
  			p.add(glComponentR);
  		}
  		frame.add(p, BorderLayout.CENTER);
  		frame.add(this.createControlPanel(), BorderLayout.SOUTH);
  		frame.pack();
  		frame.setVisible(true);
  
  		// Create and start the animator
  		boolean animate = true;
  		if (animate) {
  			animatorL = new Animator(glComponentL);
  			animatorL.setRunAsFastAsPossible(true);
  			animatorL.start();
  			if (stereoMode)
  			{
  				animatorR = new Animator(glComponentR);
  				animatorR.setRunAsFastAsPossible(true);
  				animatorR.start();
  			}
  		}
  	}
  
  	/**
  	 * Create the control panel containing the sliders for setting
  	 * the visualization parameters.
  	 * 
  	 * @return The control panel
  	 */
  	protected JPanel createControlPanel()
  	{
  		JPanel controlPanel = new JPanel(new GridLayout(3, 2));
  		JPanel panel = null;
  		JSlider slider = null;
  
  		// Density
  		panel = new JPanel(new GridLayout(1, 2));
  		panel.add(new JLabel("Density:"));
  		slider = new JSlider(0, 100, 5);
  		slider.addChangeListener(new ChangeListener()
  		{
  			public void stateChanged(ChangeEvent e)
  			{
  				JSlider source = (JSlider) e.getSource();
  				float a = source.getValue() / 100.0f;
  				density = a;
  			}
  		});
  		slider.setPreferredSize(new Dimension(0, 0));
  		panel.add(slider);
  		controlPanel.add(panel);
  
  		// Brightness
  		panel = new JPanel(new GridLayout(1, 2));
  		panel.add(new JLabel("Brightness:"));
  		slider = new JSlider(0, 100, 10);
  		slider.addChangeListener(new ChangeListener()
  		{
  			public void stateChanged(ChangeEvent e)
  			{
  				JSlider source = (JSlider) e.getSource();
  				float a = source.getValue() / 100.0f;
  				brightness = a * 10;
  			}
  		});
  		slider.setPreferredSize(new Dimension(0, 0));
  		panel.add(slider);
  		controlPanel.add(panel);
  
  		// Transfer offset
  		panel = new JPanel(new GridLayout(1, 2));
  		panel.add(new JLabel("Transfer Offset:"));
  		slider = new JSlider(0, 1000, 550);
  		slider.addChangeListener(new ChangeListener()
  		{
  			public void stateChanged(ChangeEvent e)
  			{
  				JSlider source = (JSlider) e.getSource();
  				float a = source.getValue() / 1000.0f;
  				transferOffset = (-0.5f + a) * 2;
  			}
  		});
  		slider.setPreferredSize(new Dimension(0, 0));
  		panel.add(slider);
  		controlPanel.add(panel);
  
  		// Transfer scale
  		panel = new JPanel(new GridLayout(1, 2));
  		panel.add(new JLabel("Transfer Scale:"));
  		slider = new JSlider(0, 1000, 100);
  		slider.addChangeListener(new ChangeListener()
  		{
  			public void stateChanged(ChangeEvent e)
  			{
  				JSlider source = (JSlider) e.getSource();
  				float a = source.getValue() / 1000.0f;
  				transferScale = a * 5;
  			}
  		});
  		slider.setPreferredSize(new Dimension(0, 0));
  		panel.add(slider);
  		controlPanel.add(panel);
  
  		return controlPanel;
  	}
  
  
  	
  	
  	/**
  	 * Implementation of GLEventListener: Called to initialize the
  	 * GLAutoDrawable. This method will initialize the JCudaDriver
  	 * and cause the initialization of CUDA and the OpenGL PBO.
  	 */
  	public void init(GLAutoDrawable drawable)
  	{
  		// Perform the default GL initialization
  		GL gl = drawable.getGL();
  		gl.setSwapInterval(0);
  		gl.glEnable(GL.GL_DEPTH_TEST);
  		gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  		setupView(drawable);
  
  		// Initialize the GL_ARB_pixel_buffer_object extension
  		if (!gl.isExtensionAvailable("GL_ARB_pixel_buffer_object"))
  		{
  			new Thread(new Runnable()
  			{
  				public void run()
  				{
  					JOptionPane.showMessageDialog(null,
  							"GL_ARB_pixel_buffer_object extension not available",
  							"Unavailable extension", JOptionPane.ERROR_MESSAGE);
  					runExit();
  				}
  			}).start();
  		}
  
  		// Create a TextRenderer for the status messages
  		renderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, 12));
  
  		if (initialized)
  		{
  			return;
  		}
  
  		try {
  			// find gl compatible device
  	        device = OpenCLUtil.getStaticContext().getMaxFlopsDevice();
  	        // create OpenCL context before creating any OpenGL objects
  	        // you want to share with OpenCL (AMD driver requirement)
  	        glCtx = CLGLContext.create(drawable.getContext(), device);
  
  	        // create the program
  	        program = glCtx.createProgram(getClass().getResourceAsStream("volumeRender.cl")).build();
  	        System.out.println(program.getBuildStatus());
  	        System.out.println(program.isExecutable());
  	        System.out.println(program.getBuildLog());
  	        
  	        commandQueue = device.createCommandQueue();
  	        
  	        kernelFunction = program.createCLKernel("d_render");
  	        
  		} catch (Exception e) {
  			e.printStackTrace();
  			unload();
  		}
  
  		// Initialize CUDA with the current volume data
  		initCL();
  
  		// Initialize the OpenGL pixel buffer object
  		initPBO(gl);
  		
  		initialized = true;
  	}
  
  	
  	void initVolumeBuffer(){
  		hvolumeBuffer = glCtx.createIntBuffer(h_volume.length, Mem.READ_ONLY);
  		for (int i = 0; i < h_volume.length; i++) {
  			hvolumeBuffer.getBuffer().put(i, (int)h_volume[i]);
  		}
  		hvolumeBuffer.getBuffer().rewind();
  		
  		CLImageFormat format = new CLImageFormat(ChannelOrder.RGBA, ChannelType.UNORM_INT8);
  		tex = glCtx.createImage3d(hvolumeBuffer.getBuffer(), volumeSize[0], volumeSize[1], volumeSize[2], format, Mem.READ_ONLY, Mem.ALLOCATE_BUFFER); 		
  		hvolumeBuffer.release();
  		
  		try {
  			commandQueue.putWriteImage(tex, true)
  			.finish();
  		} catch (Exception e) {
  			e.printStackTrace();
  			unload();
  		}
  		
  		hvolumeBuffer = null;
  	}
  	
  	/**
  	 * Initialize CUDA and the 3D texture with the current volume data.
  	 */
  	void initCL()
  	{
  		
  		initVolumeBuffer();
  		
  		transferFctBuffer = glCtx.createFloatBuffer(transferFunc.length, Mem.READ_ONLY);
  		transferFctBuffer.getBuffer().put(transferFunc);
  		transferFctBuffer.getBuffer().rewind();
  		
  		CLImageFormat formatTransFct = new CLImageFormat(ChannelOrder.RGBA, ChannelType.FLOAT);
  		transferTex = glCtx.createImage2d(transferFctBuffer.getBuffer(), transferFunc.length/4, 1, formatTransFct, Mem.READ_ONLY, Mem.ALLOCATE_BUFFER);
  		transferFctBuffer.release();
  		
  		try {
  			commandQueue.putWriteImage(transferTex, true)
  			.finish();
  		} catch (Exception e) {
  			e.printStackTrace();
  			unload();
  		}
  		
  		invViewMatrix = glCtx.createFloatBuffer(12, Mem.READ_ONLY);
  
  	}
  
  	/**
  	 * Creates a pixel buffer object (PBO) which stores the image that
  	 * is created by the kernel, and which will later be rendered 
  	 * by JOGL.
  	 * 
  	 * @param gl The GL context
  	 */
  	void initPBO(GL gl)
  	{
  		if (pbo != 0)
  		{
  			gl.glDeleteBuffers(1, new int[]{ pbo }, 0);
  			pbo = 0;
  		}
  
  		// Create and bind a pixel buffer object with the current 
  		// width and height of the rendering component.
  		int pboArray[] = new int[1];
  		gl.glGenBuffers(1, pboArray, 0);
  		pbo = pboArray[0];
  		gl.glBindBuffer(GL4bc.GL_PIXEL_UNPACK_BUFFER, pbo);
  		gl.glBufferData(GL4bc.GL_PIXEL_UNPACK_BUFFER, 
  				width * height * 4, null, GL.GL_DYNAMIC_DRAW);
  		gl.glBindBuffer(GL4bc.GL_PIXEL_UNPACK_BUFFER, 0);
  		
  		
  		d_output = glCtx.createFromGLBuffer(pbo, 
  				width * height * 4, 	
  				CLGLBuffer.Mem.WRITE_ONLY);
  	}
  
  	/**
  	 * Integral division, rounding the result to the next highest integer.
  	 * 
  	 * @param a Dividend
  	 * @param b Divisor
  	 * @return a/b rounded to the next highest integer.
  	 */
  	int iDivUp(int a, int b)
  	{
  		return (a % b != 0) ? (a / b + 1) : (a / b);
  	}
  
  	/**
  	 * Set up a default view for the given GLAutoDrawable
  	 * 
  	 * @param drawable The GLAutoDrawable to set the view for
  	 */
  	protected void setupView(GLAutoDrawable drawable)
  	{
  		GL4bc gl = (GL4bc) drawable.getGL();
  
  		gl.glViewport(0, 0, drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
  
  		gl.glMatrixMode(GL4bc.GL_MODELVIEW);
  		gl.glLoadIdentity();
  
  		gl.glMatrixMode(GL4bc.GL_PROJECTION);
  		gl.glLoadIdentity();
  		gl.glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
  	}
  
  	/**
  	 * Returns the given (address) value, adjusted to have
  	 * the given alignment. In newer versions of JCuda, this
  	 * function is also available as JCudaDriver#align
  	 * 
  	 * @param value The address value
  	 * @param alignment The desired alignment
  	 * @return The aligned address value
  	 */
  	protected static int align(int value, int alignment)
  	{
  		return (((value) + (alignment) - 1) & ~((alignment) - 1));		
  	}
  
  	
  	protected void setKernelParameters(){
  		// Set up the execution parameters for the kernel:
  		// - One pointer for the output that is mapped to the PBO
  		// - Two ints for the width and height of the image to render
  		// - Four floats for the visualization parameters of the renderer
  		kernelFunction.rewind()
  					  .putArg(d_output)
  					  .putArg(width)
  					  .putArg(height)
  					  .putArg(density)
  					  .putArg(brightness)
  					  .putArg(transferOffset)
  					  .putArg(transferScale)
  					  .putArg(invViewMatrix)
  					  .putArg(tex)
  					  .putArg(transferTex)
  					  .rewind();
  	}
  	
  	
  	/**
  	 * Call the kernel function, rendering the 3D volume data image
  	 * into the PBO
  	 */
  	protected void render()
  	{
  		
  		// Set up the execution parameters for the kernel:
  		setKernelParameters();
  		
  		
  		int[] realLocalSize = {Math.min(device.getMaxWorkGroupSize(),blockSize[0]), Math.min(device.getMaxWorkGroupSize(),blockSize[1])};
  		// rounded up to the nearest multiple of localWorkSize
  		int[] globalWorkSize = {OpenCLUtil.roundUp(realLocalSize[0], width), OpenCLUtil.roundUp(realLocalSize[1], height)}; 
  
  
  		try {
  	        commandQueue.putAcquireGLObject(d_output)
  	        .put2DRangeKernel(kernelFunction, 0, 0, globalWorkSize[0], globalWorkSize[1], 0, 0)
  	        .putReleaseGLObject(d_output)
  	        .finish();
  		} catch (Exception e) {
  			e.printStackTrace();
  			unload();
  		}
  	}
  
  
  
  
  
  	/**
  	 * Implementation of GLEventListener: Called when the given GLAutoDrawable
  	 * is to be displayed.
  	 */
  	public void display(GLAutoDrawable drawable)
  	{
  		if (!initialized)
  		{
  			return;
  		}
  		if (pbo == 0)
  		{
  			return;
  		}
  
  		GL4bc gl = (GL4bc) drawable.getGL();
  
  		float eyeDelta = 0;
  		if (stereoMode)
  		{
  			if (drawable == glComponentL)
  			{
  				eyeDelta = 4f;
  			}
  			else
  			{
  				eyeDelta = -4f;
  			}
  		}
  
  	
  		
  		// Use OpenGL to build view matrix
  		float modelView[] = new float[16];
  		gl.glMatrixMode(GL4bc.GL_MODELVIEW);
  		gl.glPushMatrix();
  		gl.glLoadIdentity();
  		gl.glRotatef(-rotationX, 1.0f, 0.0f, 0.0f);
  		gl.glRotatef(-(rotationY+eyeDelta), 0.0f, 1.0f, 0.0f);
  		gl.glTranslatef(-translationX, -translationY, -translationZ);
  		gl.glGetFloatv(GL4bc.GL_MODELVIEW_MATRIX, modelView, 0);
  
  
  		gl.glCullFace(GL.GL_FRONT_AND_BACK);
  
  
  
  		gl.glPopMatrix();
  
  		float[] loc_invViewMatrix = new float[12];
  		// Build the inverted view matrix
  		loc_invViewMatrix[0] = modelView[0];
  		loc_invViewMatrix[1] = modelView[4];
  		loc_invViewMatrix[2] = modelView[8];
  		loc_invViewMatrix[3] = modelView[12];
  		loc_invViewMatrix[4] = modelView[1];
  		loc_invViewMatrix[5] = modelView[5];
  		loc_invViewMatrix[6] = modelView[9];
  		loc_invViewMatrix[7] = modelView[13];
  		loc_invViewMatrix[8] = modelView[2];
  		loc_invViewMatrix[9] = modelView[6];
  		loc_invViewMatrix[10] = modelView[10];
  		loc_invViewMatrix[11] = modelView[14];
  		
  		invViewMatrix.getBuffer().rewind();
  		invViewMatrix.getBuffer().put(loc_invViewMatrix);
  		invViewMatrix.getBuffer().rewind();
  		
  		// Copy the inverted view matrix to the global variable that
  		// was obtained from the module. The inverted view matrix
  		// will be used by the kernel during rendering.
  		try {
  			commandQueue.putWriteBuffer(invViewMatrix, true)
  						.finish();
  		} catch (Exception e) {
  			e.printStackTrace();
  			unload();
  		}
  		
  		
  
  		// Render and fill the PBO with pixel data
  		render();
  
  		// Draw the image from the PBO
  		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
  		gl.glDisable(GL.GL_DEPTH_TEST);
  		gl.glRasterPos2i(0, 0);
  		gl.glBindBuffer(GL4bc.GL_PIXEL_UNPACK_BUFFER, pbo);
  		gl.glDrawPixels(width, height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, 0);
  		gl.glBindBuffer(GL4bc.GL_PIXEL_UNPACK_BUFFER, 0);
  
  		if (!stereoMode)
  		{
  			// Compute FPS 
  			long nanoTime = System.nanoTime();
  			double frameTimeMs = (nanoTime - prevFrameNanoTime) / 1000000.0;
  			prevFrameNanoTime = nanoTime;
  			double fps = 1000.0 / frameTimeMs;
  			String fpsString = String.format("%.2f", fps);
  
  			// Print status message
  			renderer.beginRendering(drawable.getSurfaceWidth(), drawable.getSurfaceHeight());
  			renderer.setColor(1.0f, 1.0f, 1.0f, 0.5f);
  			renderer.draw(fpsString + " fps", 20, 10);
  			renderer.endRendering();
  		}
  
  	}
  	
  	/**
  	 * release all CL related objects and free memory
  	 */
  	protected void unload(){
  		if (commandQueue != null)
  			commandQueue.release();
  		//release all buffers
  		if (tex != null)
  			tex.release();
  		if (transferTex != null)
  			transferTex.release();
  		if (invViewMatrix != null)
  			invViewMatrix.release();
  		if (d_output != null)
  			d_output.release();
  		if (hvolumeBuffer != null)
  			hvolumeBuffer.release();
  		if (kernelFunction != null)
  			kernelFunction.release();
  		if (program != null)
  			program.release();
  		if (glCtx != null)
  			glCtx.release();
  	}
  	
  
  	/**
  	 * Implementation of GLEventListener: Called then the GLAutoDrawable was
  	 * reshaped
  	 */
  	public void reshape(
  			GLAutoDrawable drawable, int x, int y, int width, int height)
  	{
  		this.width = width;
  		this.height = height;
  
  		initPBO(drawable.getGL());
  
  		setupView(drawable);
  	}
  
  	/**
  	 * Implementation of GLEventListener - not used
  	 */
  	public void displayChanged(
  			GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged)
  	{}
  
  	/**
  	 * Stops the animator and calls System.exit() in a new Thread.
  	 * (System.exit() may not be called synchronously inside one 
  	 * of the JOGL callbacks)
  	 */
  	protected void runExit()
  	{
  		
  		new Thread(new Runnable()
  		{
  			public void run()
  			{
  				animatorL.stop();
  				if (animatorR != null)
  				{
  					animatorR.stop();
  				}
  				unload();
  				System.exit(0);
  			}
  		}).start();
  	}
  
  
  	public void updateRotationX(double increment) {
  		this.rotationX += increment;
  	}
  
  
  	public void updateRotationY(double increment) {
  		this.rotationY += increment;
  	}
  
  
  	public void updateTranslationX(double increment) {
  		this.translationX += increment;
  	}
  
  
  	public void updateTranslationY(double increment) {
  		this.translationY += increment;
  	}
  
  
  	public void updateTranslationZ(double increment) {
  		this.translationZ += increment;
  	}
  
  
  	public void dispose(GLAutoDrawable arg0) {
  		// TODO Auto-generated method stub
  		
  	}
  }
  /*
   * Copyright (C) 2010-2014 Martin Berger
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  