package edu.stanford.rsl.conrad.cuda; /* * 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 javax.media.opengl.*; import javax.media.opengl.awt.GLJPanel; import javax.swing.*; import javax.swing.event.*; import jcuda.*; import jcuda.driver.*; import jcuda.runtime.dim3; import com.jogamp.opengl.util.Animator; import com.jogamp.opengl.util.awt.TextRenderer; /** * 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 JCudaDriverTextureSample extends JFrame implements GLEventListener, MouseControlable { private static final long serialVersionUID = 3072546722050331601L; /** * 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() //{ JCudaDriverTextureSample sample = new JCudaDriverTextureSample( 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 CUDA module containing the kernel */ protected CUmodule module = new CUmodule(); /** * The handle for the CUDA function of the kernel that is to be called */ protected CUfunction function; /** * 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 dim3 volumeSize = new dim3(); /** * The volume data that is to be rendered */ protected byte h_volume[]; /** * The block size for the kernel execution */ protected dim3 blockSize = new dim3(16, 16, 0); /** * The grid size of the kernel execution */ protected dim3 gridSize = new dim3(width / blockSize.x, height / blockSize.y, 1); /** * The global variable of the module which stores the * inverted view matrix. */ protected CUdeviceptr c_invViewMatrix = new CUdeviceptr(); /** * The inverted view matrix, which will be copied to the global * variable of the kernel. */ private float invViewMatrix[] = new float[12]; /** * 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 3D texture reference */ private CUtexref tex = new CUtexref(); /** * The 1D transfer texture reference */ protected CUtexref transferTex = new CUtexref(); /** * 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 JCudaDriverTextureSample( byte volumeData[], int sizeX, int sizeY, int sizeZ, boolean stereoMode) { h_volume = volumeData; volumeSize.x = sizeX; volumeSize.y = sizeY; volumeSize.z = sizeZ; this.stereoMode = stereoMode; if (stereoMode) { width = 280; height = 280; } else { width = 800; height = 800; } // Create the main frame frame = new JFrame("JCuda 3D texture volume rendering sample"); //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(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, 100, 55); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider source = (JSlider) e.getSource(); float a = source.getValue() / 100.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, 100, 10); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { JSlider source = (JSlider) e.getSource(); float a = source.getValue() / 100.0f; transferScale = a * 10; } }); slider.setPreferredSize(new Dimension(0, 0)); panel.add(slider); controlPanel.add(panel); return controlPanel; } CUcontext glCtx = null; /** * 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; } // Initialize the JCudaDriver. Note that this has to be done from // the same thread that will later use the JCudaDriver API. JCudaDriver.setExceptionsEnabled(true); JCudaDriver.cuInit(0); CUdevice dev = new CUdevice(); JCudaDriver.cuDeviceGet(dev, 0); glCtx = new CUcontext(); JCudaDriver.cuGLCtxCreate(glCtx, 0, dev); // Load the CUBIN file containing the kernel JCudaDriver.cuModuleLoad(module, "volumeRender_kernel.sm_10.cubin"); // Obtain the global pointer to the inverted view matrix from // the module JCudaDriver.cuModuleGetGlobal(c_invViewMatrix, new int[1], module, "c_invViewMatrix"); // Obtain a function pointer to the kernel function. This function // will later be called in the display method of this // GLEventListener. function = new CUfunction(); JCudaDriver.cuModuleGetFunction(function, module, "_Z8d_renderPjjjffff"); // Initialize CUDA with the current volume data initCuda(); // Initialize the OpenGL pixel buffer object initPBO(gl); initialized = true; } protected void fillTextureReference(CUtexref tex, byte [] volume){ CUarray d_volumeArray = new CUarray(); // Create the 3D array that will contain the volume data // and will be accessed via the 3D texture CUDA_ARRAY3D_DESCRIPTOR allocateArray = new CUDA_ARRAY3D_DESCRIPTOR(); allocateArray.Width = volumeSize.x; allocateArray.Height = volumeSize.y; allocateArray.Depth = volumeSize.z; allocateArray.Format = CUarray_format.CU_AD_FORMAT_UNSIGNED_INT8; allocateArray.NumChannels = 1; JCudaDriver.cuArray3DCreate(d_volumeArray, allocateArray); // Copy the volume data data to the 3D array CUDA_MEMCPY3D copy = new CUDA_MEMCPY3D(); copy.srcMemoryType = CUmemorytype.CU_MEMORYTYPE_HOST; copy.srcHost = Pointer.to(volume); copy.srcPitch = volumeSize.x; copy.srcHeight = volumeSize.y; copy.dstMemoryType = CUmemorytype.CU_MEMORYTYPE_ARRAY; copy.dstArray = d_volumeArray; copy.dstPitch = volumeSize.x; copy.dstHeight = volumeSize.y; copy.WidthInBytes = volumeSize.x; copy.Height = volumeSize.y; copy.Depth = volumeSize.z; JCudaDriver.cuMemcpy3D(copy); // Create the 3D texture reference for the volume data // set its parameters JCudaDriver.cuTexRefSetFilterMode(tex, CUfilter_mode.CU_TR_FILTER_MODE_LINEAR); JCudaDriver.cuTexRefSetAddressMode(tex, 0, CUaddress_mode.CU_TR_ADDRESS_MODE_CLAMP); JCudaDriver.cuTexRefSetAddressMode(tex, 1, CUaddress_mode.CU_TR_ADDRESS_MODE_CLAMP); JCudaDriver.cuTexRefSetFormat(tex, CUarray_format.CU_AD_FORMAT_UNSIGNED_INT8, 1); JCudaDriver.cuTexRefSetFlags(tex, JCudaDriver.CU_TRSF_NORMALIZED_COORDINATES); JCudaDriver.cuTexRefSetArray(tex, d_volumeArray, JCudaDriver.CU_TRSA_OVERRIDE_FORMAT); } /** * Initialize CUDA and the 3D texture with the current volume data. */ void initCuda() { CUarray d_transferFuncArray = new CUarray(); // Obtain the 3D texture reference for the volume data from // the module, set its parameters and assign the 3D volume // data array as its reference. JCudaDriver.cuModuleGetTexRef(tex, module, "tex"); fillTextureReference(tex, h_volume); // The RGBA components of the transfer function texture // Create the 2D (float4) array that will contain the // transfer function data. CUDA_ARRAY_DESCRIPTOR ad = new CUDA_ARRAY_DESCRIPTOR(); ad.Format = CUarray_format.CU_AD_FORMAT_FLOAT; ad.Width = transferFunc.length / 4; ad.Height = 1; ad.NumChannels = 4; JCudaDriver.cuArrayCreate(d_transferFuncArray, ad); // Copy the transfer function data to the array CUDA_MEMCPY2D copy2 = new CUDA_MEMCPY2D(); copy2.srcMemoryType = CUmemorytype.CU_MEMORYTYPE_HOST; copy2.srcHost = Pointer.to(transferFunc); copy2.srcPitch = transferFunc.length * Sizeof.FLOAT; copy2.dstMemoryType = CUmemorytype.CU_MEMORYTYPE_ARRAY; copy2.dstArray = d_transferFuncArray; copy2.WidthInBytes = transferFunc.length * Sizeof.FLOAT; copy2.Height = 1; JCudaDriver.cuMemcpy2D(copy2); // Obtain the transfer texture reference from the module, // set its parameters and assign the transfer function // array as its reference. JCudaDriver.cuModuleGetTexRef(transferTex, module, "transferTex"); JCudaDriver.cuTexRefSetFilterMode(transferTex, CUfilter_mode.CU_TR_FILTER_MODE_LINEAR); JCudaDriver.cuTexRefSetAddressMode(transferTex, 0, CUaddress_mode.CU_TR_ADDRESS_MODE_CLAMP); JCudaDriver.cuTexRefSetFlags(transferTex, JCudaDriver.CU_TRSF_NORMALIZED_COORDINATES); JCudaDriver.cuTexRefSetFormat(transferTex, CUarray_format.CU_AD_FORMAT_FLOAT, 4); JCudaDriver.cuTexRefSetArray(transferTex, d_transferFuncArray, JCudaDriver.CU_TRSA_OVERRIDE_FORMAT); // Set the texture references as parameters for the function call JCudaDriver.cuParamSetTexRef(function, JCudaDriver.CU_PARAM_TR_DEFAULT, tex); JCudaDriver.cuParamSetTexRef(function, JCudaDriver.CU_PARAM_TR_DEFAULT, transferTex); } /** * 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) { JCudaDriver.cuGLUnregisterBufferObject(pbo); 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 * Sizeof.BYTE * 4, null, GL.GL_DYNAMIC_DRAW); gl.glBindBuffer(GL4bc.GL_PIXEL_UNPACK_BUFFER, 0); // Register the PBO for usage with CUDA JCudaDriver.cuGLRegisterBufferObject(pbo); // Calculate new grid size gridSize = new dim3( iDivUp(width, blockSize.x), iDivUp(height, blockSize.y), 1); } /** * 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)); } /** * Call the kernel function, rendering the 3D volume data image * into the PBO */ protected void render() { // Map the PBO to get a CUDA device pointer CUdeviceptr d_output = new CUdeviceptr(); JCudaDriver.cuGLMapBufferObject(d_output, new int[1], pbo); JCudaDriver.cuMemsetD32(d_output, 0, width * height); // 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 Pointer dOut = Pointer.to(d_output); Pointer pWidth = Pointer.to(new int[]{width}); Pointer pHeight = Pointer.to(new int[]{height}); Pointer pDensity = Pointer.to(new float[]{density}); Pointer pBrightness = Pointer.to(new float[]{brightness}); Pointer pTransferOffset = Pointer.to(new float[]{transferOffset}); Pointer pTransferScale = Pointer.to(new float[]{transferScale}); int offset = 0; offset = align(offset, Sizeof.POINTER); JCudaDriver.cuParamSetv(function, offset, dOut, Sizeof.POINTER); offset += Sizeof.POINTER; offset = align(offset, Sizeof.INT); JCudaDriver.cuParamSetv(function, offset, pWidth, Sizeof.INT); offset += Sizeof.INT; offset = align(offset, Sizeof.INT); JCudaDriver.cuParamSetv(function, offset, pHeight, Sizeof.INT); offset += Sizeof.INT; offset = align(offset, Sizeof.FLOAT); JCudaDriver.cuParamSetv(function, offset, pDensity, Sizeof.FLOAT); offset += Sizeof.FLOAT; offset = align(offset, Sizeof.FLOAT); JCudaDriver.cuParamSetv(function, offset, pBrightness, Sizeof.FLOAT); offset += Sizeof.FLOAT; offset = align(offset, Sizeof.FLOAT); JCudaDriver.cuParamSetv(function, offset, pTransferOffset, Sizeof.FLOAT); offset += Sizeof.FLOAT; offset = align(offset, Sizeof.FLOAT); JCudaDriver.cuParamSetv(function, offset, pTransferScale, Sizeof.FLOAT); offset += Sizeof.FLOAT; JCudaDriver.cuParamSetSize(function, offset); // Call the CUDA kernel, writing the results into the PBO JCudaDriver.cuFuncSetBlockShape(function, blockSize.x, blockSize.y, 1); JCudaDriver.cuLaunchGrid(function, gridSize.x, gridSize.y); JCudaDriver.cuCtxSynchronize(); JCudaDriver.cuGLUnmapBufferObject(pbo); } /** * 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(); // Build the inverted view matrix invViewMatrix[0] = modelView[0]; invViewMatrix[1] = modelView[4]; invViewMatrix[2] = modelView[8]; invViewMatrix[3] = modelView[12]; invViewMatrix[4] = modelView[1]; invViewMatrix[5] = modelView[5]; invViewMatrix[6] = modelView[9]; invViewMatrix[7] = modelView[13]; invViewMatrix[8] = modelView[2]; invViewMatrix[9] = modelView[6]; invViewMatrix[10] = modelView[10]; invViewMatrix[11] = modelView[14]; // 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. JCudaDriver.cuMemcpyHtoD(c_invViewMatrix, Pointer.to(invViewMatrix), invViewMatrix.length * Sizeof.FLOAT); // 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(); } } /** * 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(); } 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 } }