From 82cd20ed209f12e22e2f5a92719b3be2bb92d8ed Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Fri, 23 Feb 2001 05:08:10 +0000 Subject: Version 2.6.0.0 --- gl4java/swing/GLAnimJPanel.java | 92 +- gl4java/swing/GLJPanel.java | 1798 ++++++++++++++++++++------------------- 2 files changed, 955 insertions(+), 935 deletions(-) (limited to 'gl4java/swing') diff --git a/gl4java/swing/GLAnimJPanel.java b/gl4java/swing/GLAnimJPanel.java index a7af76a..bdbdd94 100644 --- a/gl4java/swing/GLAnimJPanel.java +++ b/gl4java/swing/GLAnimJPanel.java @@ -24,6 +24,25 @@ import java.lang.Math; *
         The GL4Java Documentation
  * 
+ * + *

+ * There are two ways of using a GLAnimJPanel: the {@link + * gl4java.GLEventListener} model or the subclassing model. Earlier + * versions of the system only supported the subclassing model. The + * default implementations of {@link #init}, {@link #display}, + * {@link #reshape} and {@link #doCleanup} + * now send events to GLEventListeners; they can + * still be overridden as before to support the subclassing model. + * + *

+ * If using the subclassing model, you should override the following + * methods for your needs: + *

+        init - 1st initialisation
+        display - render one frame
+        reshape - to reshape (window resize)
+        ReInit - ReInitialisation after stop for setSuspended(false)
+ * 
*

* * This code uses repaint() to fire a sDisplay call by the AWT-Event thread ! @@ -93,14 +112,6 @@ import java.lang.Math; * * Like the first animation run, this class renders a view frames (default 10) * to subtract the render time from the sleep time ! - *

- * You should overwrite the following methods for your needs: - *

-        init - 1st initialisation
-        display - render one frame
-        reshape - to reshape (window resize)
-        ReInit - ReInitialisation after stop for setSuspended(false)
- * 
* * @see gl4java.awt.GLCanvas * @version 2.0, 21. April 1999 @@ -231,53 +242,6 @@ public class GLAnimJPanel extends GLJPanel setAnimateFps(FramesPerSec); } - /** - * init should be overwritten by you, - * to enter your initialisation code - * - */ - public void init() - { - /* here we should add and initialize our JAVA components */ - - /* ... and furthet OpenGL init's - like you want to */ - - glj.gljCheckGL(); - - ReInit(); - - /* and start our working thread ... */ - start(); - } - - /** - * - * This is the rendering-method called by sDisplay - * (and sDisplay is called by paint, or by the thread directly !). - * The derived-class (Your Subclass) will redefine this, - * to draw it's own animation ! - * - *

- * - * You should set shallWeRender here, - * to signalize the animation-loop 'run' to supsend - *

- * To restart the thread, just call setSuspended(false) - * - * @see gl4java.swing.GLAnimJPanel#shallWeRender - * @see gl4java.swing.GLAnimJPanel#run - * @see gl4java.swing.GLAnimJPanel#setSuspended - * @see gl4java.awt.GLAnimCanvas#sDisplay - * @see gl4java.awt.GLAnimCanvas#paint - */ - public void display() - { - int i; - - // ... just render it - - } - /** * ReInit should be overwritten by you, * to enter your re-initialisation within setSuspended(false) @@ -362,16 +326,6 @@ public class GLAnimJPanel extends GLJPanel notify(); } - /** - * Should be set in display, - * whether to render or not while the animation loop - *

- * If shallWeRender is false, - * this thread will suspend ! - * - * @see gl4java.swing.GLAnimJPanel#display - * @see gl4java.swing.GLAnimJPanel#run - */ protected boolean shallWeRender = true; private long _fDelay = 0; @@ -383,7 +337,7 @@ public class GLAnimJPanel extends GLJPanel * The running loop for animations * which initiates the call of display * - * @see gl4java.swing.GLAnimJPanel#shallWeRender + * @see gl4java.swing.GLAnimJPanel#setSuspended * @see gl4java.swing.GLAnimJPanel#display */ public void run() @@ -563,13 +517,13 @@ public class GLAnimJPanel extends GLJPanel * fot the getFps* methods *

* this function is called automatically by - * run, if the thread is suspended via shallWeRender + * run, if the thread is suspended via setSuspended *

* All data's are print out on System.out * if verboseFps is set ! * * @see gl4java.swing.GLAnimJPanel#run - * @see gl4java.swing.GLAnimJPanel#shallWeRender + * @see gl4java.swing.GLAnimJPanel#setSuspended * @see gl4java.swing.GLAnimJPanel#resetFpsCounter * @see gl4java.swing.GLAnimJPanel#stopFpsCounter * @see gl4java.swing.GLAnimJPanel#getFps @@ -605,7 +559,7 @@ public class GLAnimJPanel extends GLJPanel * verboseFps is set to true by default ! * * @see gl4java.swing.GLAnimJPanel#run - * @see gl4java.swing.GLAnimJPanel#shallWeRender + * @see gl4java.swing.GLAnimJPanel#setSuspended * @see gl4java.swing.GLAnimJPanel#resetFpsCounter * @see gl4java.swing.GLAnimJPanel#stopFpsCounter * @see gl4java.swing.GLAnimJPanel#getFps diff --git a/gl4java/swing/GLJPanel.java b/gl4java/swing/GLJPanel.java index 1e23110..31ee17f 100644 --- a/gl4java/swing/GLJPanel.java +++ b/gl4java/swing/GLJPanel.java @@ -1,866 +1,932 @@ -package gl4java.swing; - -import gl4java.*; - -import java.awt.*; -import java.awt.image.*; -import java.awt.event.*; -import javax.swing.*; - -/** - * This is meant as an base class writing - * easy render functions. A clean usage of multi-threading compatible - * with JAVA2 is implemented in GLAnimJPanel ! - * - *

- * - * If you are interessting in further Documentation and/or - * the history of GL4Java follow the following link. - * - *

-	The GL4Java Documentation
- * 
- *

- * - * You should overwrite the following methods for your needs: - *

-        preInit - initialisation before creating GLContext
-        init - 1st initialisation after creating GLContext
-        doCleanup - OGL cleanup prior to context deletion
-        display - render your frame
-	reshape - to reshape (window resize), gljResize() is allready invoked !
- * 
- * - * To check if you can use the GLContext and GL and GLU methods, - * use the function - *
-        cvsIsInit
- * 
- *

- * IF you remove/release a GLJPanel, - * e.g. you want to close/dispose it´s Window (which contains this GLJPanel), - * you HAVE TO call: - * - *

-        cvsDispose
- * 
- * You should call this before releasing/dispose this Window ! - * Also you can overwrite this class, - * to dispose your own elements, e.g. a Frame etc. - - * but be shure that you call - * cvsDispose implementation call this one ! - * - *

- * We do override the following Canvas methods. - * - *

-        update
-        paint
- * 
- *

- * - * @see gl4java.swing.GLAnimJPanel - * @see gl4java.awt.GLCanvas - * - * @version 2.0, 21. April 1999 - * @author Sven Goethel - * - */ -public class GLJPanel extends JPanel - implements GLEnum, GLUEnum, - ComponentListener, WindowListener, MouseListener -{ - protected GLContext glj = null; - public GLFunc gl = null; - public GLUFunc glu = null; - - protected boolean mustResize = false; - - protected boolean cvsInitialized=false; - - protected boolean needCvsDispose = false; - - /** - * Visual pre-set for stencil-bit number, default: 0 - * This value is updated after a GLContext is created with the - * original updated value of GLContext ! - * - * @see gl4java.swing.GLJPanel#preInit - * @see gl4java.swing.GLJPanel#paint - */ - protected int stencilBits = 0; - - /** - * Visual pre-set for accumulator buffer size, default: 0 - * This value is updated after a GLContext is created with the - * original updated value of GLContext ! - * - * @see gl4java.swing.GLJPanel#preInit - * @see gl4java.swing.GLJPanel#paint - */ - protected int accumSize = 0; - - /** - * Visual pre-set for stereoView, default: false - * This value is updated after a GLContext is created with the - * original updated value of GLContext ! - * - * @see gl4java.swing.GLJPanel#preInit - * @see gl4java.swing.GLJPanel#paint - */ - protected boolean stereoView = false; - - /** - * Visual pre-set for RGBA usage, default: true - of course ;-) - * This value is updated after a GLContext is created with the - * original updated value of GLContext ! - * - * @see gl4java.swing.GLJPanel#preInit - * @see gl4java.swing.GLJPanel#paint - */ - protected boolean rgba = true; - - /** - * The context with witch display lists and textures will be shared. - * - * @see gl4java.swing.GLJPanel#preInit - * @see gl4java.swing.GLJPanel#paint - */ - protected GLContext sharedGLContext; - - /** - * The data to hold the offscreen pixels on the java side ! - * - * @see gl4java.swing.GLJPanel#offImagePixels - * @see gl4java.swing.GLJPanel#paint - */ - protected BufferedImage offImage = null; - - /** - * The data to hold the offscreen pixels on the GL side ! - * - * @see gl4java.swing.GLJPanel#offImage - * @see gl4java.swing.GLJPanel#paint - */ - protected byte[] offImagePixels = null; - - /** - * The custom set offscreen Size - * - * If this is set to != null, - * the offscreen pixmap is used in this size, - * not in the components-size (-> faster if smaller) - * - * @see gl4java.swing.GLJPanel#paint - * @see gl4java.swing.GLJPanel#setOffScreenSize - * @see gl4java.swing.GLJPanel#getOffScreenSize - */ - protected Dimension offScrnSize = null; - protected boolean customOffScrnSize=false; - protected boolean offScrnSizeChanged=false; - - static { - if(GLContext.loadNativeLibraries(null, null, null)==false) - System.out.println("GLJPanel could not load def. native libs."); - } - - /** - * - * Constructor - * - * @param gl_Name The name of the GLFunc implementation - * If gl_Name==null, the default class will be used ! - * - * @param glu_Name The name of the GLUFunc implementation - * If gl_LibName==null, the default class will be used ! - * - * @param layout the layout manager - * @param isDoubleBuffered the flag indicates, - * if double buffer should be used - * - */ - public GLJPanel( String gl_Name, - String glu_Name, - LayoutManager layout, boolean isDoubleBuffered - ) - { - super( layout, isDoubleBuffered ); - - if( (gl=GLContext.createGLFunc(gl_Name)) ==null) - { - System.out.println("GLFunc implementation "+gl_Name+" not created"); - } - if( (glu=GLContext.createGLUFunc(glu_Name)) ==null) - { - System.out.println("GLUFunc implementation "+glu_Name+" not created"); - } - - /* to be able for RESIZE event's */ - addComponentListener(this); - - setOpaque(false); - } - - /** - * - * Constructor - * - * @param layout the layout manager - * @param isDoubleBuffered the flag indicates, - * if double buffer should be used - * - */ - public GLJPanel( LayoutManager layout, boolean isDoubleBuffered ) - { - this(null, null, layout, isDoubleBuffered); - } - - /** - * - * Constructor - * - * Uses the default GLFunc and GLUFunc implementation ! - * - * @param isDoubleBuffered the flag indicates, - * if double buffer should be used - */ - public GLJPanel( boolean isDoubleBuffered ) - { - this(null, null, new FlowLayout(), isDoubleBuffered); - } - - /** - * - * Constructor - * - * Uses the default GLFunc and GLUFunc implementation ! - * - * @param layout the layout manager - */ - public GLJPanel(LayoutManager layout) - { - this(null, null, layout, true); - } - - /** - * - * Constructor - * - * Uses the default GLFunc and GLUFunc implementation ! - * - */ - public GLJPanel( ) - { - this(null, null, new FlowLayout(), true); - } - - /** - * Used to return the created GLContext - */ - public final GLContext getGLContext() { return glj; } - - /** - * Safe the toplevel window - */ - protected Window topLevelWindow = null; - - /** - * - * This function returns the found TopLevelWindow, - * which contains this Canvas .. - * - * @return void - * - * @see gl4java.swing.GLJPanel#paint - */ - public final Window getTopLevelWindow() - { return topLevelWindow; } - - /** - * The customers offscreen Size - * - * If this is set, - * the offscreen pixmap is used in this size, - * not in the components-size (-> faster if smaller) - * - * @see gl4java.swing.GLJPanel#offScrnSize - * @see gl4java.swing.GLJPanel#setOffScreenSize - */ - public Dimension getOffScreenSize() - { return offScrnSize; } - - /** - * The customers offscreen Size - * - * If this is set, - * the offscreen pixmap is used in this size, - * not in the components-size (-> faster if smaller) - * - * @see gl4java.swing.GLJPanel#offScrnSize - * @see gl4java.swing.GLJPanel#getOffScreenSize - */ - public void setOffScreenSize(Dimension size) - { - if((size!=null && size.equals(offScrnSize)==false) || - size!=offScrnSize - ) - { - offScrnSizeChanged=true; - offScrnSize=size; - customOffScrnSize=offScrnSize!=null; - } - } - - /** - * this function overrides the Canvas paint method ! - * - * For the first paint, - * the user function preInit is called, a GLContext is created - * and the user function init is called ! - * - * Also, if a GL Context exist, GLJPanel's sDisplay-method will be called - * to do OpenGL-rendering. - * - * The sDisplay method itself calls the display-method ! - * sDisplay is needed to be thread-safe, to manage - * the resize functionality and to safe the time per frame. - * - * To define your rendering, you should overwrite the display-method - * in your derivation. - * - * @see gl4java.GLContext#GLContext - * @see gl4java.swing.GLJPanel#cvsIsInit - * @see gl4java.swing.GLJPanel#sDisplay - * @see gl4java.swing.GLJPanel#display - * @see gl4java.swing.GLJPanel#preInit - * @see gl4java.swing.GLJPanel#init - */ - public synchronized final void paintComponent(Graphics g) - { - if(glj == null || - (mustResize && !customOffScrnSize) || offScrnSizeChanged ) - { - if(mustResize) - { - cvsDispose(); - mustResize=false; - } - preInit(); - glj = GLContext.createOffScreenCtx ( this, gl, glu, - stereoView, - rgba, stencilBits, accumSize, - sharedGLContext, - offScrnSize - ); - - if(glj!=null) - { - /* - createOwnWindow = glj.isOwnWindowCreated(); - doubleBuffer = glj.isDoubleBuffer(); - */ - stencilBits = glj.getStencilBitNumber(); - accumSize = glj.getAccumSize(); - stereoView = glj.isStereoView(); - rgba = glj.isRGBA(); - } - if(offImage!=null) - offImage.flush(); - offImage=null; - offScrnSizeChanged=false; - - init(); - - // fetch the top-level window , - // to add us as the windowListener - // - Container _c = getParent(); - Container c = null; - - while(_c!=null) - { - c = _c; - _c = _c.getParent(); - } - - if(c instanceof Window) { - topLevelWindow = (Window)c; - topLevelWindow.addComponentListener(this); - topLevelWindow.addMouseListener(this); - } else { - topLevelWindow = null; - System.out.println("toplevel is not a Window: "+c); - } - - if(topLevelWindow!=null) - { - topLevelWindow.addWindowListener(this); - } else { - System.out.println("no parent found for "+getName()); - System.out.flush(); - } - if(glj!=null && glj.gljIsInit()) - cvsInitialized=true; - } - /* - if( mustResize ) size = getSize(); - g.setClip(0, 0, size.width, size.height ); - */ - //super.paintComponent(g); - - gr = g; - sDisplay(); - } - - Graphics gr = null; - DataBufferInt dbInt = null; - - /** - * - * This is the thread save rendering-method called by paint. - * The actual thread will be set to highes priority befor calling - * 'display'. After 'display' the priority will be reset ! - * - * 'gljFree' will be NOT called after 'display'. - * - * We tested the above to use multi-threading and - * for the demonstration 'glDemos' it works ;-)) ! - * - * BE SURE, if you want to call 'display' by yourself - * (e.g. in the run method for animation) - * YOU HAVE TO CALL sDisplay -- OR YOU MUST KNOW WHAT YOU ARE DOING THEN ! - * - * @return void - * - * @see gl4java.swing.GLJPanel#paint - * @see gl4java.swing.GLJPanel#display - */ - public synchronized final void sDisplay() - { - boolean ok = true; - - long _s = System.currentTimeMillis(); - - if(!cvsIsInit()) - return; - - if( glj.gljMakeCurrent() == false ) { - System.out.println("GLJPanel: problem in use() method"); - return; - } - - if(ok) - { - display(); - _f_dur_self = System.currentTimeMillis()-_s; - if(GLContext.gljClassDebug) - { - _f_dur_self_sum+=_f_dur_self; - glj.gljCheckGL(); - } - - int glFormat; - int glComps; - int awtFormat; - - glFormat = (rgba == true)?GL_RGBA:GL_RGB; - glComps = (rgba == true)?4:3; - awtFormat = (rgba == true)?BufferedImage.TYPE_INT_ARGB: - BufferedImage.TYPE_INT_RGB; - - Dimension size = null; - if(customOffScrnSize) - size=offScrnSize; - else - size=getSize(); - int w=size.width; - int h=size.height; - - long _s_tst = System.currentTimeMillis(); - - if(offImage==null || - offImage.getHeight()!=h || offImage.getWidth()!=w) - { - if(offImage!=null) - offImage.flush(); - offImage = new BufferedImage(w,h,awtFormat); - offImagePixels=new byte[w*h*glComps]; - dbInt = (DataBufferInt) - offImage.getRaster().getDataBuffer(); - - if(GLContext.gljClassDebug) - { - System.out.print("set offImage to size: "+size+ - "(hw size: "+w+"x"+h+"), type: "); - switch(glFormat) { - case GL_RGB: System.out.println("RGB"); break; - case GL_RGBA: System.out.println("RGBA"); break; - case GL_BGR_EXT: System.out.println("BGR"); break; - case GL_BGRA_EXT: System.out.println("BGRA"); break; - } - } - } - - glj.gljReadPixelGL2AWT(0,0,w,h,glFormat,GL_UNSIGNED_BYTE, - glj.isDoubleBuffer()?GL_BACK:GL_FRONT, - offImagePixels, dbInt.getData()); - - //glj.gljSwap(); // no true swapping with offscreen buffers .. - - if(GLContext.gljClassDebug) - _f_dur_tst_sum+=System.currentTimeMillis()-_s_tst; - - if(GLContext.gljClassDebug) - glj.gljCheckGL(); - glj.gljFree(); // enable ctx for threads ... - - if(!customOffScrnSize) - gr.drawImage(offImage, 0, 0, this); - else { - size=super.getSize(); - gr.drawImage(offImage, 0, 0, size.width, size.height, this); - } - - _f_dur_total = System.currentTimeMillis()-_s; - if(GLContext.gljClassDebug) - { - _f_dur_total_sum+=_f_dur_total; - if(++_f_dur_times==100) - { - System.out.println("self p 100: "+ - (double)(_f_dur_self_sum/100)/1000.0+" s"); - - System.out.println("tst p 100: "+ - (double)(_f_dur_tst_sum/100)/1000.0+" s"); - - System.out.println("gl-bitblit p 100: "+ - (double)((_f_dur_total_sum-_f_dur_self_sum)/100)/1000.0+" s"); - System.out.println("total p 100: "+ - (double)(_f_dur_total_sum/100)/1000.0+" s"); - - _f_dur_self_sum=0; - _f_dur_tst_sum=0; - _f_dur_total_sum=0; - _f_dur_times=0; - } - } - - } - - } - - /** - * - * This is the rendering-method called by sDisplay - * (and sDisplay is called by paint !). - * The derived-class (Your Subclass) will redefine this, to draw it's own... - * - * BE SURE, if you want to call 'display' by yourself - * (e.g. in the run method for animation) - * YOU HAVE TO CALL sDisplay ! - * - * 'sDisplay' manages a semaphore to avoid reentrance of - * the display function !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * - * @return void - * - * @see gl4java.swing.GLJPanel#sDisplay - * @see gl4java.swing.GLJPanel#paint - */ - public void display() - { - } - - /** - * - * This is your pre-init method. - * preInit is called just BEFORE the GL-Context is created. - * You should override preInit, to initialize your visual-stuff, - * like the protected vars: doubleBuffer and stereoView - * - * @return void - * - * @see gl4java.swing.GLJPanel#paint - * @see gl4java.swing.GLJPanel#stereoView - * @see gl4java.swing.GLJPanel#rgba - * @see gl4java.swing.GLJPanel#stencilBits - * @see gl4java.swing.GLJPanel#accumSize - */ - public void preInit() - { - } - - /** - * - * This is your init method. - * init is called right after the GL-Context is initialized. - * You should override init, to initialize your stuff needed - * by OpenGL an Java ! - * - * @return void - * - * @see gl4java.swing.GLJPanel#paint - */ - public void init() - { - } - - /** - * This method is used to clean up any OpenGL stuff (delete textures - * or whatever) prior to actually deleting the OpenGL context. - * You should override this with your own version, if you need to do - * any cleanup work at this phase. - * This functions is called within cvsDispose - * - * @return void - * - * @see gl4java.swing.GLJPanel#cvsDispose - */ - public void doCleanup() - { - } - - /** - * This function returns, if everything is init: the GLContext, - * the and the users init function - * This value is set in the paint method! - * - * @return boolean - * - * @see gl4java.swing.GLJPanel#paint - * @see gl4java.swing.GLJPanel#init - */ - public boolean cvsIsInit() - { - return cvsInitialized; - } - - protected long _f_dur_self = 0; - protected long _f_dur_self_sum = 0; - protected long _f_dur_tst_sum = 0; - protected long _f_dur_total = 0; - protected long _f_dur_total_sum = 0; - protected int _f_dur_times = 0; - - /** - * - * This is the reshape-method called by paint. - * The derived-class (Your Subclass) will redefine this, - * to manage your individual reshape ... - * - * This ´reshape´ method will be invoked after the first paint command - * after GLJPanel.componentResize is called AND only if ´gljUse´ was - * succesfull (so a call of gljUse is redundant). - * ´reshape´ is not an overloading of java.awt.Component.reshape, - * ´reshape´ is more like ´glut´-reshape. - * - * GLJPanel.reshape allready has a simple default implementation, - * which calls ´gljResize´ and ´glViewport´ - so you may be can - * left this one as it is (no need to overload). - * The needed call to ´gljResize´ is done by hte invoker paint ! - * - * @param width the new width - * @param height the new height - * @return void - * - * @see gl4java.swing.GLJPanel#paint - * @see gl4java.swing.GLJPanel#sDisplay - */ - public void reshape( int width, int height ) - { - if(GLContext.gljClassDebug) - System.out.println("GLJPanel::reshape bounds("+getBounds()+")"); - gl.glViewport(0,0, width, height); - } - - /** - * - * ´componentResized´ is the componentListeners event handler. - * - * This method sets the variable ´mustResize´ to true, - * so the upcoming ´paint´ method-call will invoke ´reshape´ ! - * - * This little look-alike complicating thing is done, - * to avoid an Exception by using the glContext from more than - * one concurrent thread´s ! - * - * You cannot override this implementation, it is final - * - override ´reshape' instead ! - * - * @param e the element, which is resized - * @return void - * - * @see gl4java.swing.GLJPanel#paint - * @see gl4java.swing.GLJPanel#reshape - */ - public void componentResized(ComponentEvent e) - { - if(GLContext.gljClassDebug) - System.out.println("GLJPanel::componentResized("+e.getComponent()+")"); - if(glj!=null && glj.gljIsInit() && e.getComponent()==this ) - { - mustResize = true; - //repaint(); - } - } - - public void componentMoved(ComponentEvent e) - { - /* - if(GLContext.gljClassDebug) - System.out.print("GLJPanel::componentMoved("+e.getComponent()+")"); - if(e.getComponent().equals(topLevelWindow)) - { - repaint(); - } - */ - } - - public void componentShown(ComponentEvent e) - { - } - - public void componentHidden(ComponentEvent e) - { - } - - public void mouseClicked(MouseEvent e) - { - } - public void mouseEntered(MouseEvent e) - { - /* - if(GLContext.gljClassDebug) - System.out.print("GLJPanel::mouseEntered("+e.getComponent()+")"); - if(e.getComponent().equals(topLevelWindow)) - { - repaint(); - } - */ - } - public void mouseExited(MouseEvent e) - {} - public void mousePressed(MouseEvent e) - { - } - public void mouseReleased(MouseEvent e) - { - } - - public void windowOpened(WindowEvent e) - { - } - - /** - * - * ´windowClosing´ is the windowListeners event handler - * for the topLevelWindow of this Canvas ! - * - * This methods free´s AND destroy´s - * the GL Context with ´glj.gljDestroy´ ! - * - * @return void - * - */ - public void windowClosing(WindowEvent e) - { - if(e.getComponent().equals(topLevelWindow)) - { - cvsDispose(); - } - } - - /** - * - * ´windowClosed´ is the windowListeners event handler. - * - * @return void - * - */ - public void windowClosed(WindowEvent e) - { - if (needCvsDispose) cvsDispose(); - } - - public void windowIconified(WindowEvent e) - { - } - - public void windowDeiconified(WindowEvent e) - { - } - - public void windowActivated(WindowEvent e) - { - } - - public void windowDeactivated(WindowEvent e) - { - } - - /** - * You should call this before releasing/dispose this Window ! - * Also you can overwrite this class, - * to dispose your own elements, e.g. a Frame etc. - - * but be shure that you call - * cvsDispose implementation call this one ! - * - * This function calls gljDestroy of GLContext ! - * - * @see gl4java.GLContext#gljDestroy - * @see gl4java.swing.GLJPanel#doCleanup - */ - public void cvsDispose() - { - cvsInitialized = false; - if (glj != null) - { - if (glj.gljIsInit()) - { - /* Sometimes the Microsoft VM calls the - Applet.stop() method but doesn't have - permissions to do J/Direct calls, so - this whole block of code will throw a - security exception. If this happens, - however, windowClosing() will still - call us again later and we will have - another opportunity to shut down the - context, so it all works out fine. */ - try - { - glj.gljFree(); - doCleanup(); - //locks and free's GLContext - glj.setEnabled(false); - glj.gljDestroy(); - needCvsDispose = false; - } - catch (Exception ex) - { - needCvsDispose = true; - } - } - } - - // Setting glj to null will simply cause paint() to re-initialize. - // We don't want that to happen, so we will leave glj non-null. - } - - public Dimension getSize() - { - if(customOffScrnSize) - return offScrnSize; - return super.getSize(); - } - - /** - * get methods - */ - public final int cvsGetWidth() { - return getSize().width; - } - public final int cvsGetHeight() { - return getSize().height; - } -} - +package gl4java.swing; + +import gl4java.*; +import gl4java.drawable.*; +import gl4java.drawable.utils.*; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; + +/** + * This is meant as an base class writing + * easy render functions. A clean usage of multi-threading compatible + * with JAVA2 is implemented in GLAnimJPanel ! + * + *

+ * + * If you are interessting in further Documentation and/or + * the history of GL4Java follow the following link. + * + *

+	The GL4Java Documentation
+ * 
+ *

+ * + * There are two ways of using a GLJPanel: the {@link + * gl4java.GLEventListener} model or the subclassing model. Earlier + * versions of the system only supported the subclassing model. The + * default implementations of {@link #init}, {@link #display}, + * {@link #reshape} and {@link #doCleanup} + * now send events to GLEventListeners; they can + * still be overridden as before to support the subclassing model. + * + *

+ * If using the subclassing model, you should override the following + * methods for your needs: + *

+        preInit - initialisation before creating GLContext
+        init - 1st initialisation after creating GLContext
+        doCleanup - OGL cleanup prior to context deletion
+        display - render your frame
+	reshape - to reshape (window resize), gljResize() is allready invoked !
+ * 
+ * + * To check if you can use the GLContext and GL and GLU methods, + * use the function + *
+        cvsIsInit
+ * 
+ *

+ * IF you remove/release a GLJPanel, + * e.g. you want to close/dispose it´s Window (which contains this GLJPanel), + * you HAVE TO call: + * + *

+        cvsDispose
+ * 
+ * You should call this before releasing/dispose this Window ! + * Also you can overwrite this class, + * to dispose your own elements, e.g. a Frame etc. - + * but be shure that you call + * cvsDispose implementation call this one ! + * + *

+ * We do override the following Canvas methods. + * + *

+        update
+        paint
+ * 
+ *

+ * + * @see gl4java.swing.GLAnimJPanel + * @see gl4java.awt.GLCanvas + * + * @version 2.0, 21. April 1999 + * @author Sven Goethel + * */ +public class GLJPanel extends JPanel + implements GLEnum, GLUEnum, + ComponentListener, WindowListener, MouseListener, + GLDrawable +{ + protected GLContext glj = null; + public GLFunc gl = null; + public GLUFunc glu = null; + + protected boolean mustResize = false; + + protected boolean cvsInitialized=false; + + protected boolean needCvsDispose = false; + + /** + * Visual pre-set for stencil-bit number, default: 0 + * This value is updated after a GLContext is created with the + * original updated value of GLContext ! + * + * @see gl4java.swing.GLJPanel#preInit + * @see gl4java.swing.GLJPanel#paint + */ + protected int stencilBits = 0; + + /** + * Visual pre-set for accumulator buffer size, default: 0 + * This value is updated after a GLContext is created with the + * original updated value of GLContext ! + * + * @see gl4java.swing.GLJPanel#preInit + * @see gl4java.swing.GLJPanel#paint + */ + protected int accumSize = 0; + + /** + * Visual pre-set for stereoView, default: false + * This value is updated after a GLContext is created with the + * original updated value of GLContext ! + * + * @see gl4java.swing.GLJPanel#preInit + * @see gl4java.swing.GLJPanel#paint + */ + protected boolean stereoView = false; + + /** + * Visual pre-set for RGBA usage, default: true - of course ;-) + * This value is updated after a GLContext is created with the + * original updated value of GLContext ! + * + * @see gl4java.swing.GLJPanel#preInit + * @see gl4java.swing.GLJPanel#paint + */ + protected boolean rgba = true; + + /** + * The context with witch display lists and textures will be shared. + * + * @see gl4java.swing.GLJPanel#preInit + * @see gl4java.swing.GLJPanel#paint + */ + protected GLContext sharedGLContext; + + /** + * The data to hold the offscreen pixels on the java side ! + * + * @see gl4java.swing.GLJPanel#offImagePixels + * @see gl4java.swing.GLJPanel#paint + */ + protected BufferedImage offImage = null; + + /** + * The data to hold the offscreen pixels on the GL side ! + * + * @see gl4java.swing.GLJPanel#offImage + * @see gl4java.swing.GLJPanel#paint + */ + protected byte[] offImagePixels = null; + + /** + * The custom set offscreen Size + * + * If this is set to != null, + * the offscreen pixmap is used in this size, + * not in the components-size (-> faster if smaller) + * + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.swing.GLJPanel#setOffScreenSize + * @see gl4java.swing.GLJPanel#getOffScreenSize + */ + protected Dimension offScrnSize = null; + protected boolean customOffScrnSize=false; + protected boolean offScrnSizeChanged=false; + + // The list of GLEventListeners + private GLEventListenerList listeners = new GLEventListenerList(); + + static { + if(GLContext.loadNativeLibraries(null, null, null)==false) + System.out.println("GLJPanel could not load def. native libs."); + } + + /** + * + * Constructor + * + * @param gl_Name The name of the GLFunc implementation + * If gl_Name==null, the default class will be used ! + * + * @param glu_Name The name of the GLUFunc implementation + * If gl_LibName==null, the default class will be used ! + * + * @param layout the layout manager + * @param isDoubleBuffered the flag indicates, + * if double buffer should be used + * + */ + public GLJPanel( String gl_Name, + String glu_Name, + LayoutManager layout, boolean isDoubleBuffered + ) + { + super( layout, isDoubleBuffered ); + + if( (gl=GLContext.createGLFunc(gl_Name)) ==null) + { + System.out.println("GLFunc implementation "+gl_Name+" not created"); + } + if( (glu=GLContext.createGLUFunc(glu_Name)) ==null) + { + System.out.println("GLUFunc implementation "+glu_Name+" not created"); + } + + /* to be able for RESIZE event's */ + addComponentListener(this); + + setOpaque(false); + } + + /** + * + * Constructor + * + * @param layout the layout manager + * @param isDoubleBuffered the flag indicates, + * if double buffer should be used + * + */ + public GLJPanel( LayoutManager layout, boolean isDoubleBuffered ) + { + this(null, null, layout, isDoubleBuffered); + } + + /** + * + * Constructor + * + * Uses the default GLFunc and GLUFunc implementation ! + * + * @param isDoubleBuffered the flag indicates, + * if double buffer should be used + */ + public GLJPanel( boolean isDoubleBuffered ) + { + this(null, null, new FlowLayout(), isDoubleBuffered); + } + + /** + * + * Constructor + * + * Uses the default GLFunc and GLUFunc implementation ! + * + * @param layout the layout manager + */ + public GLJPanel(LayoutManager layout) + { + this(null, null, layout, true); + } + + /** + * + * Constructor + * + * Uses the default GLFunc and GLUFunc implementation ! + * + */ + public GLJPanel( ) + { + this(null, null, new FlowLayout(), true); + } + + /** + * Used to return the created GLContext + */ + public final GLContext getGLContext() { return glj; } + + /** + * Safe the toplevel window + */ + protected Window topLevelWindow = null; + + /** + * + * This function returns the found TopLevelWindow, + * which contains this Canvas .. + * + * @return void + * + * @see gl4java.swing.GLJPanel#paint + */ + public final Window getTopLevelWindow() + { return topLevelWindow; } + + /** + * The customers offscreen Size + * + * If this is set, + * the offscreen pixmap is used in this size, + * not in the components-size (-> faster if smaller) + * + * @see gl4java.swing.GLJPanel#offScrnSize + * @see gl4java.swing.GLJPanel#setOffScreenSize + */ + public Dimension getOffScreenSize() + { return offScrnSize; } + + /** + * The customers offscreen Size + * + * If this is set, + * the offscreen pixmap is used in this size, + * not in the components-size (-> faster if smaller) + * + * @see gl4java.swing.GLJPanel#offScrnSize + * @see gl4java.swing.GLJPanel#getOffScreenSize + */ + public void setOffScreenSize(Dimension size) + { + if((size!=null && size.equals(offScrnSize)==false) || + size!=offScrnSize + ) + { + offScrnSizeChanged=true; + offScrnSize=size; + customOffScrnSize=offScrnSize!=null; + } + } + + /** + * this function overrides the Canvas paint method ! + * + * For the first paint, + * the user function preInit is called, a GLContext is created + * and the user function init is called ! + * + * Also, if a GL Context exist, GLJPanel's sDisplay-method will be called + * to do OpenGL-rendering. + * + * The sDisplay method itself calls the display-method ! + * sDisplay is needed to be thread-safe, to manage + * the resize functionality and to safe the time per frame. + * + * To define your rendering, you should overwrite the display-method + * in your derivation. + * + * @see gl4java.GLContext#GLContext + * @see gl4java.swing.GLJPanel#cvsIsInit + * @see gl4java.swing.GLJPanel#sDisplay + * @see gl4java.swing.GLJPanel#display + * @see gl4java.swing.GLJPanel#preInit + * @see gl4java.swing.GLJPanel#init + */ + public synchronized final void paintComponent(Graphics g) + { + if(glj == null || + (mustResize && !customOffScrnSize) || offScrnSizeChanged ) + { + if(mustResize) + { + cvsDispose(); + mustResize=false; + } + preInit(); + glj = GLContext.createOffScreenCtx ( this, gl, glu, + stereoView, + rgba, stencilBits, accumSize, + sharedGLContext, + offScrnSize + ); + + if(glj!=null) + { + /* + createOwnWindow = glj.isOwnWindowCreated(); + doubleBuffer = glj.isDoubleBuffer(); + */ + stencilBits = glj.getStencilBitNumber(); + accumSize = glj.getAccumSize(); + stereoView = glj.isStereoView(); + rgba = glj.isRGBA(); + } + if(offImage!=null) + offImage.flush(); + offImage=null; + offScrnSizeChanged=false; + + init(); + + // fetch the top-level window , + // to add us as the windowListener + // + Container _c = getParent(); + Container c = null; + + while(_c!=null) + { + c = _c; + _c = _c.getParent(); + } + + if(c instanceof Window) { + topLevelWindow = (Window)c; + topLevelWindow.addComponentListener(this); + topLevelWindow.addMouseListener(this); + } else { + topLevelWindow = null; + System.out.println("toplevel is not a Window: "+c); + } + + if(topLevelWindow!=null) + { + topLevelWindow.addWindowListener(this); + } else { + System.out.println("no parent found for "+getName()); + System.out.flush(); + } + if(glj!=null && glj.gljIsInit()) + cvsInitialized=true; + } + /* + if( mustResize ) size = getSize(); + g.setClip(0, 0, size.width, size.height ); + */ + //super.paintComponent(g); + + gr = g; + sDisplay(); + } + + Graphics gr = null; + DataBufferInt dbInt = null; + + /** + * + * This is the thread save rendering-method called by paint. + * The actual thread will be set to highes priority befor calling + * 'display'. After 'display' the priority will be reset ! + * + * 'gljFree' will be NOT called after 'display'. + * + * We tested the above to use multi-threading and + * for the demonstration 'glDemos' it works ;-)) ! + * + * BE SURE, if you want to call 'display' by yourself + * (e.g. in the run method for animation) + * YOU HAVE TO CALL sDisplay -- OR YOU MUST KNOW WHAT YOU ARE DOING THEN ! + * + * @return void + * + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.swing.GLJPanel#display + * @see gl4java.drawable.GLEventListener#preDisplay + * @see gl4java.drawable.GLEventListener#display + * @see gl4java.drawable.GLEventListener#postDisplay + */ + public synchronized final void sDisplay() + { + boolean ok = true; + + long _s = System.currentTimeMillis(); + + if(!cvsIsInit()) + return; + + listeners.sendPreDisplayEvent(this); + + if( glj.gljMakeCurrent() == false ) { + System.out.println("GLJPanel: problem in use() method"); + return; + } + + if(ok) + { + display(); + _f_dur_self = System.currentTimeMillis()-_s; + if(GLContext.gljClassDebug) + { + _f_dur_self_sum+=_f_dur_self; + glj.gljCheckGL(); + } + + int glFormat; + int glComps; + int awtFormat; + + glFormat = (rgba == true)?GL_RGBA:GL_RGB; + glComps = (rgba == true)?4:3; + awtFormat = (rgba == true)?BufferedImage.TYPE_INT_ARGB: + BufferedImage.TYPE_INT_RGB; + + Dimension size = null; + if(customOffScrnSize) + size=offScrnSize; + else + size=getSize(); + int w=size.width; + int h=size.height; + + long _s_tst = System.currentTimeMillis(); + + if(offImage==null || + offImage.getHeight()!=h || offImage.getWidth()!=w) + { + if(offImage!=null) + offImage.flush(); + offImage = new BufferedImage(w,h,awtFormat); + offImagePixels=new byte[w*h*glComps]; + dbInt = (DataBufferInt) + offImage.getRaster().getDataBuffer(); + + if(GLContext.gljClassDebug) + { + System.out.print("set offImage to size: "+size+ + "(hw size: "+w+"x"+h+"), type: "); + switch(glFormat) { + case GL_RGB: System.out.println("RGB"); break; + case GL_RGBA: System.out.println("RGBA"); break; + case GL_BGR_EXT: System.out.println("BGR"); break; + case GL_BGRA_EXT: System.out.println("BGRA"); break; + } + } + } + + glj.gljReadPixelGL2AWT(0,0,w,h,glFormat,GL_UNSIGNED_BYTE, + glj.isDoubleBuffer()?GL_BACK:GL_FRONT, + offImagePixels, dbInt.getData()); + + //glj.gljSwap(); // no true swapping with offscreen buffers .. + + if(GLContext.gljClassDebug) + _f_dur_tst_sum+=System.currentTimeMillis()-_s_tst; + + if(GLContext.gljClassDebug) + glj.gljCheckGL(); + glj.gljFree(); // enable ctx for threads ... + + if(!customOffScrnSize) + gr.drawImage(offImage, 0, 0, this); + else { + size=super.getSize(); + gr.drawImage(offImage, 0, 0, size.width, size.height, this); + } + + _f_dur_total = System.currentTimeMillis()-_s; + if(GLContext.gljClassDebug) + { + _f_dur_total_sum+=_f_dur_total; + if(++_f_dur_times==100) + { + System.out.println("self p 100: "+ + (double)(_f_dur_self_sum/100)/1000.0+" s"); + + System.out.println("tst p 100: "+ + (double)(_f_dur_tst_sum/100)/1000.0+" s"); + + System.out.println("gl-bitblit p 100: "+ + (double)((_f_dur_total_sum-_f_dur_self_sum)/100)/1000.0+" s"); + System.out.println("total p 100: "+ + (double)(_f_dur_total_sum/100)/1000.0+" s"); + + _f_dur_self_sum=0; + _f_dur_tst_sum=0; + _f_dur_total_sum=0; + _f_dur_times=0; + } + } + } + + listeners.sendPostDisplayEvent(this); + + } + + /** + * + * This is the rendering-method called by sDisplay + * (and sDisplay is called by paint !). + * + *

+ * The default implementation of display() sends display events to + * all {@link gl4java.GLEventListener}s associated with this + * GLJPanel, and automatically calls {@link + * gl4java.GLContext#gljMakeCurrent} and {@link + * gl4java.GLContext#gljFree} as necessary. + * + *

} + * If you use the subclassing model (as opposed to the + * GLEventListener model), your subclass will redefine this to + * perform its OpenGL drawing. + * + *

+ * BE SURE, if you want to call 'display' by yourself + * (e.g. in the run method for animation) + * YOU HAVE TO CALL sDisplay ! + * + * 'sDisplay' manages a semaphore to avoid reentrance of + * the display function !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * + * @return void + * + * @see gl4java.swing.GLJPanel#sDisplay + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.drawable.GLEventListener#display + */ + public void display() + { + listeners.sendDisplayEvent(this); + } + + /** + * + * This is your pre-init method. + * preInit is called just BEFORE the GL-Context is created. + * You should override preInit, to initialize your visual-stuff, + * like the protected vars: doubleBuffer and stereoView + * + * @return void + * + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.swing.GLJPanel#stereoView + * @see gl4java.swing.GLJPanel#rgba + * @see gl4java.swing.GLJPanel#stencilBits + * @see gl4java.swing.GLJPanel#accumSize + */ + public void preInit() + { + } + + /** + * + * init is called right after the GL-Context is initialized. + * The default implementation calls init() on all of this + * component's GLEventListeners. + * + *

+ * If using the subclassing model, you can override this to + * perform one-time OpenGL initializations such as setting up + * lights and display lists. + * + * @return void + * + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.drawable.GLEventListener#init + */ + public void init() + { + listeners.sendInitEvent(this); + } + + /** + * This method is used to clean up any OpenGL stuff (delete textures + * or whatever) prior to actually deleting the OpenGL context. + * You should override this with your own version, if you need to do + * any cleanup work at this phase. + * This functions is called within cvsDispose + * + * @return void + * + * @see gl4java.swing.GLJPanel#cvsDispose + * @see gl4java.drawable.GLEventListener#cleanup + */ + public void doCleanup() + { + listeners.sendCleanupEvent(this); + } + + /** + * This function returns, if everything is init: the GLContext, + * the and the users init function + * This value is set in the paint method! + * + * @return boolean + * + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.swing.GLJPanel#init + */ + public boolean cvsIsInit() + { + return cvsInitialized; + } + + protected long _f_dur_self = 0; + protected long _f_dur_self_sum = 0; + protected long _f_dur_tst_sum = 0; + protected long _f_dur_total = 0; + protected long _f_dur_total_sum = 0; + protected int _f_dur_times = 0; + + /** + * + * This ´reshape´ method will be invoked after the first paint command + * after GLCanvas.componentResize is called AND only if ´gljMakeCurrent´ was + * successful (so a call of gljMakeCurrent is redundant). + * ´reshape´ is not an overloading of java.awt.Component.reshape, + * ´reshape´ is more like ´glut´-reshape. + * + *

+ * GLCanvas.reshape already has a simple default implementation, + * which calls ´gljResize´ and ´glViewport´. It also sends the + * reshape() event to all GLEventListeners. If using the + * GLEventListener model, it may not be necessary to do anything + * in your event listener's reshape() method; if using the + * subclassing model, it may not be necessary to override this. + * + *

+ * The needed call to ´gljResize´ is done by the invoker paint ! + * + * @param width the new width + * @param height the new height + * @return void + * + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.swing.GLJPanel#sDisplay + * @see gl4java.drawable.GLEventListener#reshape + */ + public void reshape( int width, int height ) + { + if(GLContext.gljClassDebug) + System.out.println("GLJPanel::reshape bounds("+getBounds()+")"); + gl.glViewport(0,0, width, height); + listeners.sendReshapeEvent(this, width, height); + } + + /** + * + * ´componentResized´ is the componentListeners event handler. + * + * This method sets the variable ´mustResize´ to true, + * so the upcoming ´paint´ method-call will invoke ´reshape´ ! + * + * This little look-alike complicating thing is done, + * to avoid an Exception by using the glContext from more than + * one concurrent thread´s ! + * + * You cannot override this implementation, it is final + * - override ´reshape' instead ! + * + * @param e the element, which is resized + * @return void + * + * @see gl4java.swing.GLJPanel#paint + * @see gl4java.swing.GLJPanel#reshape + */ + public void componentResized(ComponentEvent e) + { + if(GLContext.gljClassDebug) + System.out.println("GLJPanel::componentResized("+e.getComponent()+")"); + if(glj!=null && glj.gljIsInit() && e.getComponent()==this ) + { + mustResize = true; + //repaint(); + } + } + + public void componentMoved(ComponentEvent e) + { + /* + if(GLContext.gljClassDebug) + System.out.print("GLJPanel::componentMoved("+e.getComponent()+")"); + if(e.getComponent().equals(topLevelWindow)) + { + repaint(); + } + */ + } + + public void componentShown(ComponentEvent e) + { + } + + public void componentHidden(ComponentEvent e) + { + } + + public void mouseClicked(MouseEvent e) + { + } + public void mouseEntered(MouseEvent e) + { + /* + if(GLContext.gljClassDebug) + System.out.print("GLJPanel::mouseEntered("+e.getComponent()+")"); + if(e.getComponent().equals(topLevelWindow)) + { + repaint(); + } + */ + } + public void mouseExited(MouseEvent e) + {} + public void mousePressed(MouseEvent e) + { + } + public void mouseReleased(MouseEvent e) + { + } + + public void windowOpened(WindowEvent e) + { + } + + /** + * + * ´windowClosing´ is the windowListeners event handler + * for the topLevelWindow of this Canvas ! + * + * This methods free´s AND destroy´s + * the GL Context with ´glj.gljDestroy´ ! + * + * @return void + * + */ + public void windowClosing(WindowEvent e) + { + if(e.getComponent().equals(topLevelWindow)) + { + cvsDispose(); + } + } + + /** + * + * ´windowClosed´ is the windowListeners event handler. + * + * @return void + * + */ + public void windowClosed(WindowEvent e) + { + if (needCvsDispose) cvsDispose(); + } + + public void windowIconified(WindowEvent e) + { + } + + public void windowDeiconified(WindowEvent e) + { + } + + public void windowActivated(WindowEvent e) + { + } + + public void windowDeactivated(WindowEvent e) + { + } + + /** + * You should call this before releasing/dispose this Window ! + * Also you can overwrite this class, + * to dispose your own elements, e.g. a Frame etc. - + * but be shure that you call + * cvsDispose implementation call this one ! + * + * This function calls gljDestroy of GLContext ! + * + * @see gl4java.GLContext#gljDestroy + * @see gl4java.swing.GLJPanel#doCleanup + */ + public void cvsDispose() + { + cvsInitialized = false; + if (glj != null) + { + if (glj.gljIsInit()) + { + /* Sometimes the Microsoft VM calls the + Applet.stop() method but doesn't have + permissions to do J/Direct calls, so + this whole block of code will throw a + security exception. If this happens, + however, windowClosing() will still + call us again later and we will have + another opportunity to shut down the + context, so it all works out fine. */ + try + { + glj.gljFree(); + doCleanup(); + //locks and free's GLContext + glj.setEnabled(false); + glj.gljDestroy(); + needCvsDispose = false; + } + catch (Exception ex) + { + needCvsDispose = true; + } + } + } + + // Setting glj to null will simply cause paint() to re-initialize. + // We don't want that to happen, so we will leave glj non-null. + } + + public Dimension getSize() + { + if(customOffScrnSize) + return offScrnSize; + return super.getSize(); + } + + /** + * get methods + */ + public final int cvsGetWidth() { + return getSize().width; + } + public final int cvsGetHeight() { + return getSize().height; + } + + //---------------------------------------------------------------------- + // Implementation of GLDrawable + // + + public void addGLEventListener(GLEventListener listener) { + listeners.add(listener); + } + + public void removeGLEventListener(GLEventListener listener) { + listeners.remove(listener); + } + + public GLFunc getGL() { + return gl; + } + + public GLUFunc getGLU() { + return glu; + } +} -- cgit v1.2.3