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 { Component compHeavy = null; protected GLContext glj = null; public GLFunc gl = null; public GLUFunc glu = null; protected Dimension size = null; protected boolean mustResize = false; protected boolean mustMove = false; protected boolean needCvsDispose = false; /** * Visual pre-set for doubleBuffer, default: true * This value is updated after a GLContext is created with the * original updated value of GLContext ! * * @see gl4java.awt.GLCanvas#preInit * @see gl4java.awt.GLCanvas#paint */ protected boolean doubleBuffer = 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.awt.GLCanvas#preInit * @see gl4java.awt.GLCanvas#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 ! * * This value has a special behavior. * For input - within the contructor, * it is the value for each component ! * * The output value, after the constructor returns, * it is the summary of all accumulation bits of all components ! * * @see gl4java.awt.GLCanvas#preInit * @see gl4java.awt.GLCanvas#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.awt.GLCanvas#preInit * @see gl4java.awt.GLCanvas#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.awt.GLCanvas#preInit * @see gl4java.awt.GLCanvas#paint */ protected boolean rgba = true; protected GLCapabilities capabilities = null; /** * 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.awt.GLCanvas#preInit * @see gl4java.awt.GLCanvas#paint */ protected boolean createOwnWindow = false; /** * The context with wich display lists and textures will be shared. * * @see gl4java.awt.GLCanvas#preInit * @see gl4java.awt.GLCanvas#paint */ protected GLContext sharedGLContext; /** * The data to hold the offscreen pixels on the java side ! * * @see gl4java.swing.GLJPanel#paint */ protected BufferedImage offImage = null; protected Point absCoord = null; protected int offImageWidth = 0; protected int offImageHeight= 0; protected int glFormat=0; protected int glType=0; protected int glComps=0; protected int awtFormat=0; /** * 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 { GLContext.gljNativeDebug = true; GLContext.gljClassDebug = true; if(GLContext.doLoadNativeLibraries("GL4JavaJauGljJNI12", 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 || ( !glj.gljIsInit() && isGLEnabled() ) ) { if(GLContext.gljClassDebug) System.out.println("GLCanvas create GLContext (recreate="+ (glj != null) +")"); preInit(); if(glj!=null) glj=null; compHeavy = this; // fetch the heavy peer component in temporary var. comp while(compHeavy!=null && (compHeavy.getPeer() instanceof java.awt.peer.LightweightPeer) ) compHeavy=compHeavy.getParent(); if (capabilities != null ) { glj = new GLContext ( this, gl, glu, capabilities, sharedGLContext ); } else { glj = new GLContext ( this, gl, glu, createOwnWindow, doubleBuffer, stereoView, rgba, stencilBits, accumSize, sharedGLContext ); } if(glj!=null) { createOwnWindow = glj.isOwnWindowCreated(); doubleBuffer = glj.isDoubleBuffer(); stencilBits = glj.getStencilBitNumber(); accumSize = glj.getAccumSize(); stereoView = glj.isStereoView(); rgba = glj.isRGBA(); } 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); } 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(); } /* to be able for RESIZE event's */ addComponentListener(this); addMouseListener(this); /* force a reshape, to be sure .. */ mustResize = true; } gr = g; sDisplay(); super.paintComponent(g); } Graphics gr = null; DataBufferInt dbInt = null; DataBufferUShort dbUShort = null; DataBufferByte dbByte = 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; if(!cvsIsInit()) return; absCoord = getAbsoluteCoord(); int absX = (int)absCoord.getX(); int absY = (int)absCoord.getY(); listeners.sendPreDisplayEvent(this); if( glj.gljMakeCurrent() == false ) { System.out.println("GLJPanel: problem in use() method"); return; } if( mustResize ) { Dimension hsize = compHeavy.getSize(); Dimension size = getSize(); glj.gljResize( hsize.width, hsize.height ) ; resize(absX, hsize.height-absY-size.height, size.width, size.height); mustResize = false; invalidate(); //repaint(100); } if( mustMove ) { Dimension hsize = compHeavy.getSize(); resize(absX, hsize.height-absY-size.height, size.width, size.height); mustMove = false; invalidate(); } long _s = System.currentTimeMillis(); if(ok) { display(); listeners.sendPostDisplayEvent(this); } glj.gljFree(); glj.gljSwap(); _f_dur = System.currentTimeMillis()-_s; } /** * * 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.awt.GLCanvas#paint * @see gl4java.awt.GLCanvas#init */ public boolean cvsIsInit() { if(glj!=null) return glj.gljIsInit(); return false; } /** * This function queries, if the GL-Context is enabled ! * * @return boolean * * @see gl4java.GLContext#isEnabled * @see gl4java.GLContext#gljMakeCurrent */ public boolean isGLEnabled() { if(glj!=null) return glj.isEnabled(); return false; } protected long _f_dur = 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); } public void resize( int x, int y, int width, int height ) { if(GLContext.gljClassDebug) System.out.println("GLJPanel::reshape bounds("+getBounds()+")"); gl.glViewport(x,y, 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(glj!=null && glj.gljIsInit() && e.getComponent()==this ) { mustMove = true; //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() { 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; } public Point getAbsoluteCoord() { Container obj = this; Container next = obj.getParent(); Point _absCoord = this.getLocation(); Point p = null; //System.out.println("\nADDING START :"+obj); while ( next instanceof JComponent ) { obj = next; next = obj.getParent(); //System.out.println("\nADDING :"+obj); p = obj.getLocation(); _absCoord.x+=p.x; _absCoord.y+=p.y; } return _absCoord; } }