//package gl4java.awt; import gl4java.*; import java.awt.*; import java.awt.event.*; public class GLPanel extends Panel 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 doubleBuffer, default: true * This value is updated after a GLContext is created with the * original updated value of GLContext ! * * @see GLPanel#preInit * @see GLPanel#paint */ protected boolean doubleBuffer = true; /** * 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 GLPanel#preInit * @see GLPanel#paint */ protected int stencilBits = 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 GLPanel#preInit * @see GLPanel#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 GLPanel#preInit * @see GLPanel#paint */ protected boolean rgba = true; /** * 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 GLPanel#preInit * @see GLPanel#paint */ protected boolean createOwnWindow = false; /** * The context with witch display lists and textures will be shared. * * @see GLPanel#preInit * @see GLPanel#paint */ protected GLContext sharedGLContext; static { if(GLContext.loadNativeLibraries(null, null, null)==false) System.out.println("GLPanel could not load def. native libs."); } /** * * Constructor * * @param width the canvas initial-prefered width * @param height the canvas initial-prefered height * * @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 ! * */ public GLPanel( String gl_Name, String glu_Name ) { super( new BorderLayout() ); 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); } /** * * Constructor * * Uses the default GLFunc and GLUFunc implementation ! * * @param width the canvas initial-prefered width * @param height the canvas initial-prefered height * */ public GLPanel( ) { this(null, null); } /** * Used to return the created GLContext */ public final GLContext getGLContext() { return glj; } /** * * Overridden update * This one only call's the paint method, without clearing * the background - thats hopefully done by OpenGL ;-) * * @param g the Graphics Context * @return void * * @see GLPanel#paint */ public void update(Graphics g) { //JAU if(GLContext.gljClassDebug) System.out.println("GPanel::update(): isShowing()="+isShowing()); if(isShowing()) { /* let's let OpenGL clear the background ... */ paint(g); } } /** * Safe the toplevel window */ protected Window topLevelWindow = null; /** * * This function returns the found TopLevelWindow, * which contains this Canvas .. * * @return void * * @see GLPanel#paint */ public final Window getTopLevelWindow() { return topLevelWindow; } /** * 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, GLPanel'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 GLPanel#cvsIsInit * @see GLPanel#sDisplay * @see GLPanel#display * @see GLPanel#preInit * @see GLPanel#init */ public synchronized final void paint( Graphics g ) { //JAU if(GLContext.gljClassDebug) System.out.println("GPanel::paint()"); if(glj == null ) { preInit(); glj = new GLContext ( this, gl, glu, createOwnWindow, doubleBuffer, stereoView, rgba, stencilBits, sharedGLContext ); if(glj!=null) { createOwnWindow = glj.isOwnWindowCreated(); doubleBuffer = glj.isDoubleBuffer(); stencilBits = glj.getStencilBitNumber(); 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); 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 ); */ sPaint(g); } /** * * 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 GLPanel#paint * @see GLPanel#display */ public synchronized final void sPaint( Graphics g ) { boolean ok = true; long _s = System.currentTimeMillis(); if(!cvsIsInit()) return; /* if( glj.gljMakeCurrent() == false ) { System.out.println("GLPanel: problem in use() method"); return; } */ if( mustResize ) { Dimension size = getSize(); glj.gljResize( size.width, size.height ) ; reshape(size.width, size.height); mustResize = false; } if(ok) { display(); if(GLContext.gljClassDebug) glj.gljCheckGL(); _f_dur_self = System.currentTimeMillis()-_s; /* GLGraphics glg1 = (GLGraphics)glg.create(); try { super.paint(glg1); } finally { glg1.dispose(); } */ super.paint(g); if(GLContext.gljClassDebug) glj.gljCheckGL(); _f_dur_total = System.currentTimeMillis()-_s; glj.gljSwap(); glj.gljFree(); } } /** * * 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 GLPanel#sDisplay * @see GLPanel#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 GLPanel#paint * @see GLPanel#doubleBuffer * @see GLPanel#stereoView * @see GLPanel#rgba * @see GLPanel#stencilBits */ 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 GLPanel#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 GLPanel#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 GLPanel#paint * @see GLPanel#init */ public boolean cvsIsInit() { return cvsInitialized; } protected long _f_dur_self = 0; protected long _f_dur_total = 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 GLPanel.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. * * GLPanel.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 GLPanel#paint * @see GLPanel#sDisplay */ public void reshape( int width, int height ) { if(GLContext.gljClassDebug) System.out.println("GLPanel::reshape bounds("+getBounds()+")"); gl.glViewport(0,0, width, height); gl.glMatrixMode(GL_PROJECTION); gl.glLoadIdentity(); gl.glOrtho(0, width, height, 0, -50.0,50.0); gl.glMatrixMode(GL_MODELVIEW); } /* JAU */ /* public final void setBounds(int x, int y, int width, int height) { reshape(x, y, width, height); } public final void setBounds(Rectangle r) { reshape(r.x, r.y, r.width, r.height); } public void reshape(int x, int y, int width, int height) { super.reshape(x, y, width, height); if(glj==null || !glj.gljIsInit() || !glj.gljMakeCurrent()) { System.out.println("GLPanel::setBounds(): problem in use() method"); return; } reshape(width-x, height-y); glj.gljCheckGL(); glj.gljFree(); size = new Dimension(width-x, height-y); } */ /** * * ´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 GLPanel#paint * @see GLPanel#reshape */ public void componentResized(ComponentEvent e) { if(GLContext.gljClassDebug) System.out.println("GLPanel::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("GLPanel::componentMoved("+e.getComponent()+")"); /* if(e.getComponent().equals(topLevelWindow)) { repaint(); } */ } public void componentShown(ComponentEvent e) { if(GLContext.gljClassDebug) System.out.print("GLPanel::componentShown("+e.getComponent()+")"); } public void componentHidden(ComponentEvent e) { if(GLContext.gljClassDebug) System.out.print("GLPanel::componentHidden("+e.getComponent()+")"); } public void mouseClicked(MouseEvent e) { if(GLContext.gljClassDebug) System.out.print("."); } public void mouseEntered(MouseEvent e) { if(GLContext.gljClassDebug) System.out.print("GLPanel::mouseEntered("+e.getComponent()+")"); /* if(e.getComponent().equals(topLevelWindow)) { repaint(); } */ } public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) { if(GLContext.gljClassDebug) System.out.print("<"); } public void mouseReleased(MouseEvent e) { if(GLContext.gljClassDebug) System.out.print(">"); } 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 GLPanel#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. } /** * get methods */ public final int cvsGetWidth() { return getSize().width; } public final int cvsGetHeight() { return getSize().height; } }