package gl4java.swing; import gl4java.*; import gl4java.drawable.*; import gl4java.drawable.utils.*; import java.awt.*; import java.awt.geom.*; 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#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 { if(GLContext.doLoadNativeLibraries(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(true); } /** * * 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 paint(Graphics g) */ public synchronized final void paintComponent(Graphics g) { if(glj == null || (mustResize && !customOffScrnSize) || offScrnSizeChanged ) { if(mustResize) { cvsDispose(); } 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(); Dimension size = size=getSize(); reshape(size.width, size.height); // 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; } gr = g; sDisplay(); } 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; long _s = System.currentTimeMillis(); if(!cvsIsInit()) return; listeners.sendPreDisplayEvent(this); if( glj.gljMakeCurrent() == false ) { System.out.println("GLJPanel: problem in use() method"); return; } if(mustResize) { mustResize=false; Dimension size = null; if(customOffScrnSize) size=offScrnSize; else size=getSize(); reshape(size.width, size.height); /* invalidate(); repaint(100); */ } if(ok) { display(); _f_dur_self = System.currentTimeMillis()-_s; if(GLContext.gljClassDebug) { _f_dur_self_sum+=_f_dur_self; glj.gljCheckGL(); } Dimension size = null; Dimension csize = null; Point cloc = null; int absX = 0; int absY = 0; if(customOffScrnSize) size=offScrnSize; else size=getSize(); int w=size.width; int h=size.height; long _s_tst = System.currentTimeMillis(); sun.awt.image.BufferedImageGraphics2D big2d = (sun.awt.image.BufferedImageGraphics2D) gr; cloc = getLocation(null); Rectangle2D rect = new Rectangle2D.Float(); rect.setRect(0, 0, w, h); ((Graphics2D)gr).clip(rect); /* if(offImage==null || offImage.getHeight()!=h || offImage.getWidth()!=w) if(true) */ if(offImage!=big2d.bufImg) { offImage = big2d.bufImg; awtFormat = offImage.getType(); absCoord = getAbsoluteCoord(null); absX = (int)absCoord.getX(); absY = (int)absCoord.getY(); csize = getSize(); offImageWidth = offImage.getWidth(); offImageHeight= offImage.getHeight(); System.out.println("c size= "+csize); System.out.println("c loc = "+cloc); System.out.println("abs pos ="+absX+"/"+absY); System.out.println("bufImg size ="+offImageWidth+"x"+offImageHeight); dbByte=null; dbUShort=null; dbInt=null; switch (awtFormat) { case BufferedImage.TYPE_3BYTE_BGR: System.out.println("awt=3BYTE_BGR, gl=BGR,UNSIGNED_BYTE"); glFormat = GL_BGR; glType = GL_UNSIGNED_BYTE; glComps = 3; dbByte = (DataBufferByte) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_4BYTE_ABGR: System.out.println("awt=4BYTE_ABGR, gl=BGRA,UNSIGNED_INT_8_8_8_8"); glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8; glComps = 4; dbByte = (DataBufferByte) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_INT_RGB: System.out.println("awt=INT_RGB, gl=BGRA,UNSIGNED_INT_8_8_8_8_REV"); glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8_REV; glComps = 4; dbInt = (DataBufferInt) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_INT_ARGB: System.out.println("awt=INT_ARGB, gl=BGRA,INT_8_8_8_8_REV"); glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8_REV; glComps = 4; dbInt = (DataBufferInt) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_INT_BGR: System.out.println("awt=INT_BGR, gl=BGRA,UNSIGNED_INT_8_8_8_8"); glFormat = GL_BGRA; glType = GL_UNSIGNED_INT_8_8_8_8; glComps = 4; dbInt = (DataBufferInt) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_USHORT_555_RGB: System.out.println("awt=USHORT_555_RGB, gl=RGBA,UNSIGNED_INT_5_5_5_1"); glFormat = GL_RGBA; glType = GL_UNSIGNED_SHORT_5_5_5_1; glComps = 2; dbUShort = (DataBufferUShort) offImage.getRaster().getDataBuffer(); break; case BufferedImage.TYPE_USHORT_565_RGB: System.out.println("awt=USHORT_565_RGB, gl=RGB,UNSIGNED_INT_5_6_5"); glFormat = GL_RGB; glType = GL_UNSIGNED_SHORT_5_6_5; glComps = 2; dbUShort = (DataBufferUShort) offImage.getRaster().getDataBuffer(); break; } } else { absCoord = getAbsoluteCoord(getRootPane()); absX = (int)absCoord.getX(); absY = (int)absCoord.getY(); } if(dbByte!=null) glj.gljReadPixelGL2AWT(0,0,w,h,glFormat,glType, glj.isDoubleBuffer()?GL_BACK:GL_FRONT, dbByte.getData()); else if(dbUShort!=null) glj.gljReadPixelGL2AWT(0,0,w,h,glFormat,glType, glj.isDoubleBuffer()?GL_BACK:GL_FRONT, dbUShort.getData()); else if(dbInt!=null) glj.gljReadPixelGL2AWT(offImageWidth, absX, absY, 0, 0, w, h, glFormat,glType, glj.isDoubleBuffer()?GL_BACK:GL_FRONT, 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; } public Point getAbsoluteCoord(JRootPane rp) { 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(); if ( next instanceof JComponent ) { //System.out.println("\nADDING :"+obj); p = obj.getLocation(); _absCoord.x+=p.x; _absCoord.y+=p.y; } if(obj==rp) break; } return _absCoord; } }